Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Different ways to strongly-type function component props with TypeScript

April 07, 2020
reacttypescript

One of the most beneficial parts of using TypeScript with React is the ability to have strongly-typed component props. This allows developers using the component to quickly understand what they can pass and helps them avoid making a mistake.

In this post, we will go through several different approaches to strongly-typing a function component props. We are going to strongly-type the props for the following component:

const TextField = ({
  label,
  text,
  onTextChange
}) => (
  <div>
    <label>{label}</label>
    <input
      type="text"
      value={text}
      onChange={e =>
        onTextChange(e.currentTarget.value)
      }
    />
  </div>
);

Inline type annotation

Perhaps the simplest approach is to use an inline type annotation on the props function parameter:

const TextField = ({
  label,
  text,
  onTextChange
}: {  label: string;  text: string;  onTextChange: (text: string) => void;}) => ...

This is a nice approach when there are only a couple of props in our component, and the props don’t need to be reused.

This can also be used if the component is a regular function rather than an arrow function:

function TextField({
  label,
  text,
  onTextChange
}: {  label: string,  text: string,  onTextChange: (text: string) => void}) {  ...
}

Type alias

We could extract the props into a type alias:

type Props = {  label: string;  text: string;  onTextChange: (text: string) => void;};const TextField = ({
  label,
  text,
  onTextChange
}: Props) => ...

This is great when there are at least a few props, or we want to reuse the props type across multiple components.

Again, this approach can be used for regular function components:

function TextField({
  label,
  text,
  onTextChange
}: Props) ...

Interface

We can use an interface instead of a type alias:

interface Props {
  label: string;
  text: string;
  onTextChange: (text: string) => void;
}

Interfaces have very similar capabilities to type aliases these days, so it’s generally a personal preference for which to use.

FC type

There is a standard React type, FC, that we can use on arrow function components. “FC” stands for Function Component, and it aliases a type called FunctionComponent.

const TextField: React.FC<Props> = ({  label,
  text,
  onTextChange
}) => ...

The FC type is used on the variable assigned to the arrow function. It is a generic type that we pass the components props type into.

There are some arguable minor benefits to using the FC type:

  • FC provides some type safety on defaultProps, which can be used to provide default values for the props. However, defaultProps may be removed from React in a future release.
  • FC includes the children prop type, so you don’t have to define it explicitly. However, it is arguably better to explicitly define this so that consumers know for sure whether this is available.

Extending a standard type

The props type can extend a standard type:

interface Props
  extends React.InputHTMLAttributes<    HTMLInputElement  > {  label: string;
  text: string;
  onTextChange: (text: string) => void;
}

In the above example, we have extended the standard props type for an input element. This means our component now accepts props such as maxLength and placeholder.

Alternatively we can use a type alias and intersection to achieve the same effect:

type Props = React.InputHTMLAttributes<  HTMLInputElement> & {  label: string;
  text: string;
  onTextChange: (text: string) => void;
};

Want to learn more about using TypeScript with React? Check out my course
Using TypeScript with React