background image which represents - How to optimize your react {...other} props type definitions

How to optimize your react {...other} props type definitions

October 03, 2019
react, typescript, blog, about

If you are creating reusable components or for example a design system in react you sometimes want to pass down more props then you usually have defined in your type definitions.

This pattern is often used in component libraries like material ui or polaris.

    <div {...rest} />
    // or
    <div {...other} />

When you write your components in TypeScript you have to explicitly define that you can pass down further props. Let`s say that someone wants to use your component and apply any additional global HTML attributes which are allowed to be used on the rendered div wrapper of the Chip component below.

import * as React from 'react';

export interface ChipProps {
  /**
   * The background color of the chip.
   */
  backgroundColor?: string;
}

/**
 * A component may highlight additional text information for any content.
 */
export class Chip extends React.Component<ChipProps, {}> {
  static defaultProps: ChipProps = {
    backgroundColor: '#011626',
  };

  render() {
    const { children, style, backgroundColor, ...other } = this.props;
    return (
      <div {...other} style={{ ...style, background: backgroundColor }}>        {children}
      </div>
    );
  }
}

export default Chip;

With the definition above you will see that you get will get a typing error since either the style the children or the {...other} props have been properly defined. You will see that TypeScript will throw an error and will tell you that you cannot apply styles for example since it is not defined in the type definition of the Chip component.

In the next example, you see on line three that we extend our previous type definition, to inherit typings from the div HTML element. Now TypeScript knows that all the other props are allowed which are appropriate HTML div attributes. All other props which are not valid HTML div element attributes like for example value are permitted.

import * as React from 'react';

export interface ChipProps extends React.HTMLAttributes<HTMLDivElement> {  /**
   * The background color of the chip.
   */
  backgroundColor?: string;

  /**
   * The text color of the chip.
   */
  textColor?: string;
}

const propTypes: React.ValidationMap<ChipProps> = {};

/**
 * A `Chip` may highlight additional text information for any content, e.g.
 * a label or tag with special meaning.
 */
export class Chip extends React.Component<ChipProps, {}> {
  static propTypes = propTypes;
  static defaultProps: ChipProps = {
    backgroundColor: '#011626',
  };

  render() {
    const { children, className, style, backgroundColor, ...other } = this.props;

    return (
      <div {...other} className={componentClassName} style={{ ...style, background: backgroundColor }}>
        {children}
      </div>
    );
  }
}

export default Chip;
<Chip value="">My Chip</Chip> // this will still throw an typing error

<Chip id="">My Chip</Chip> // will work since id is an appropriate HTML div attribute

Let’s say that you have another wrapper element like a span or a input tag. Then you can extend yor typings by just using the other appropriate React HTML element types.

<span {...other} > // you would extend from -> React.HTMLAttributes<HTMLSpanElement> in your typing definition

<input {...other} > // you would extend from ->  React.InputHTMLAttributes<HTMLInputElement> in your typing defintion

If you want to check out a live example you can open up the CodeSandbox editor.