Adding Types to Props and State in React using TypeScript

avatar

Borislav Hadzhiev

Wed Apr 21 20212 min read

banner

Photo by Ricardo Rocha

Updated on Wed Apr 21 2021

How to use typescript to specify the types of props and state in react.js

Adding Types to Props in React #

Lets start with adding props to a component, the simplest way has to do with just adding types to a function, nothing specific to react:

export const Company = () => {
  return <Employee name="Tim" salary={5000} />;
};

type EmployeeProps = {
  name: string;
  salary: number;
};

const Employee = ({name, salary}: EmployeeProps) => {
  return (
    <div>
      {name} earns {salary} $.
    </div>
  );
};

In the above snippet the Employee component takes in the name and salary props, just like you would add types to an object parameter in a function.

However with the above signature we're not telling typescript that this is a react component. We would want to specifically tell typescript that this is a react component so we get an error if we forget the return statement. The snippet would then look like:

- const Employee = ({name, salary}: EmployeeProps) => {
+ const Employee: React.FC<EmployeeProps> = ({name, salary}) => {
  return (
    <div>
      {name} earns {salary} $.
    </div>
  );
};

The difference being we're now telling typescript that:

  • Employee is a React function component
  • Employee might have properties assigned to it, for example: propTypes, contextTypes, displayName etc
  • Employee will receive props of type EmployeeProps
  • the children prop is defined on the FC type as React.ReactNode, so you can use it automatically without having to explicitly type it

Adding Types to State in React #

When keeping track of primitive values in state with the useState hook typescript can infer the type when you provide an initial value:

import {useState} from 'react';

export const Employee: React.FC = () => {
  const [name, setName] = useState('');

  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)} />
      <h3>Employee: {name}</h3>
    </div>
  );
};

In the above snippet typescript knows that the name variable is going to be of type string, so setting it to any other type would error.

If we however had an array or an object as a state value, providing an empty array as an initial argument to useState would initialize the state variable to never[], meaning an array that is always going to be empty, which is not what we want.

import {useState} from 'react';

export const Employee: React.FC = () => {
  const [name, setName] = useState('');
  const [allNames, setAllNames] = useState([]); // <-- allNames are now never[] because typescript can't infer the future value

  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)} />
      <h3>{allNames}</h3>
      <button onClick={() => setAllNames(prev => [...prev, name])}>
        Add name
      </button>
    </div>
  );
};

To help typescript figure out the type of value we intend to store we can pass a generic to useState:

export const Employee: React.FC = () => {
  const [name, setName] = useState('');
- const [allNames, setAllNames] = useState([]);
+ const [allNames, setAllNames] = useState<string[]>([]);

Typescript now knows the type of values the allNames variable will store, even though it had no way to infer it initially.

Further Reading #

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee