Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend HTML attributes in React with Typescript

When wrapping simple HTML elements, take for example this MyLink tag:

// MyLink.tsx
import React from 'react';
import { Omit } from 'type-zoo';

interface MyLinkProps
  extends Omit<?, 'href'> {
  to: string;
  children: React.ReactNode;
}

export const MyLinkProps: React.FC<MyLinkProps> = ({
  children,
  to,
  ...attributes
}) => (
  <a href={to} {...attributes}>
    {children}
  </a>
);

// Elsewhere
<MyLink to="https://example.com">Example</MyLink>

It seems I can swap out the ? with any of the following:

  • React.HTMLProps<HTMLAnchorElement>
  • React.HTMLAttributes<HTMLAnchorElement>
  • React.AnchorHTMLAttributes<HTMLAnchorElement>

But what should one actually be using here? And if it varies, how does one choose?

like image 217
Svish Avatar asked Apr 28 '19 16:04

Svish


People also ask

Does React support all HTML attributes?

As of React 16, any standard or custom DOM attributes are fully supported. These props work similarly to the corresponding HTML attributes, with the exception of the special cases documented above. You may also use custom attributes as long as they're fully lowercase.

How do you extend props in React?

To extend an HTML Element in a component's props in React, extend the specific element type in the props interface of your component, e.g. interface ButtonProps extends React. ButtonHTMLAttributes<HTMLButtonElement> {} . Copied! import React from 'react'; interface ButtonProps extends React.

Can you extend React components?

Using the extends keyword, you can allow the current component to access all the component's properties, including the function, and trigger it from the child component. This example creates one component called ParentClass. jsx. ParentClass extends the component from React as React.

How do you pass rest props in TypeScript?

const {a, b, c} = props; const htmlProps = rest(props, {a, b, c}); And once TypeScript supports object rest/spread I can just look for all usages of rest() and simplify it to const {a, b, c, ... htmlProps} = props . Save this answer.


2 Answers

I would say React.HTMLProps<HTMLAnchorElement> since this is the most descriptive type. For example if you wanted to provide a ref property to your anchor tag, you would need to use this type.

like image 99
Daniel Cottone Avatar answered Sep 19 '22 15:09

Daniel Cottone


I would choose React.AnchorHTMLAttributes<HTMLAnchorElement> even if you are simply wrapping html elements because it is the interface that extends the most and can guard against typescript errors

In this example, I'm wrapping an html <a> element in styled component.

MyLink.tsx:

import { forwardRef, AnchorHTMLAttributes, HTMLAttributes, HTMLProps } from 'react'
import styled, { StyledComponent } from '@emotion/styled'
import { EmotionJSX } from '@emotion/react/types/jsx-namespace'

type AnchorProps = AnchorHTMLAttributes<HTMLAnchorElement>
// type AnchorProps = HTMLAttributes<HTMLAnchorElement>
// type AnchorProps = HTMLProps<HTMLAnchorElement> // <~ THIS CAUSE ERROR

const StyledAnchor: StyledComponent<AnchorProps> = styled.a`
  color: red;
`

export default forwardRef<
  HTMLAnchorElement,
  AnchorHTMLAttributes<HTMLAnchorElement>
>((props: AnchorProps): EmotionJSX.Element => <StyledAnchor {...props} />)


// Elsewhere

const ref = useRef<HTMLAnchorElement>(null)

<MyLink ref={ref} href="/">my link</MyLink>

if I choose React.HTMLProps<HTMLAnchorElement>, then I will get typescript error:

Type 'StyledComponent<{ theme?: Theme | undefined; as?: ElementType<any> | undefined; }, DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, {}>' is not assignable to type 'StyledComponent<AnchorProps, {}, {}>'.
  Types of property 'propTypes' are incompatible.
    Type 'WeakValidationMap<{ theme?: Theme | undefined; as?: ElementType<any> | undefined; } & ClassAttributes<HTMLAnchorElement> & AnchorHTMLAttributes<HTMLAnchorElement>> | undefined' is not assignable to type 'WeakValidationMap<AnchorProps> | undefined'.
      Type 'WeakValidationMap<{ theme?: Theme | undefined; as?: ElementType<any> | undefined; } & ClassAttributes<HTMLAnchorElement> & AnchorHTMLAttributes<HTMLAnchorElement>>' is not assignable to type 'WeakValidationMap<AnchorProps>'.
        Types of property 'as' are incompatible.
          Type 'Validator<ElementType<any> | null | undefined> | undefined' is not assignable to type 'Validator<string | null | undefined> | undefined'.
            Type 'Validator<ElementType<any> | null | undefined>' is not assignable to type 'Validator<string | null | undefined>'.
              Type 'ElementType<any> | null | undefined' is not assignable to type 'string | null | undefined'.
                Type 'ComponentClass<any, any>' is not assignable to type 'string'.ts(2322)

So I always use React.AnchorHTMLAttributes<HTMLAnchorElement>

Here are the react type interfaces:

  • interface HTMLProps<T> extends AllHTMLAttributes<T>, ClassAttributes<T>
  • interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T>
  • interface AnchorHTMLAttributes<T> extends HTMLAttributes<T>
like image 44
kimbaudi Avatar answered Sep 17 '22 15:09

kimbaudi