React Component Props with Typescript

When you have used React with JavaScript before and are now confronted with writing props for your component, there might come up some questions on how to do this. In this article, I want to show you how to define the shape of your props and how to set default values for your optional props.

Let’s start with the class component. A class component is created by extending React.Component<P, S>.

Defining properties’ shape

React.Component<P, S> is a templated class. P defines the shape of your properties, while S on the other hand defines the shape of the component’s state. So lets say we want to define a component for a TodoItem. Our TodoItem should show a checkmark, if the TodoItem has been done or none, if we still have to do it. A text tells us what to do. It is good to think about how our component should be used first.

<TodoItem done={true} text="Do the dishes" />

So we want the user of our component to give us the current status of the todo item and its text. Let’s define this in our TodoItem Component.

interface TodoItemProps { 
done: boolean;
text: string;
}
class TodoItem extends React.Component<TodoItemProps, {}> {
render() {
const { done, text } = this.props;
...
}
}

First, we define a TodoItem class and extended it from React.Component. Then we define the shape of the values, the user should give us from outside in an interface named TodoItemProps and pass that to the template argument named P in the React.Component<P, S>. As we have no state in this component, we pass in {} as the value for the template argument S in our component.

Defining default properties

So now the user has to give us all the properties defined in TodoItemProps. They are mandatory, otherwise TypeScript will tell us that some props are missing. But what if we want all TodoItems not to be done by default. So that the user of our TodoItemComponent can omit the property done? To achieve this, we make the property done optional and define a default value for this property.

<TodoItem text="Do the dishes" />
interface TodoItemProps {
done?: boolean;
text: string;
}
class TodoItem extends React.Component<TodoItemProps, {}> {
static defaultProps: Partial<TodoItemProps> = {
done: false,
}
   render() { 
const { done, text } = this.props;
...
}
}

For this to work, we make the done in our TodoItemProps optional by appending a ? at the props name. That means this property is optional. To define a default value for the property done, we create a static variable in our class named defaultProps.

Now comes the tricky part: We want type safety for our default props. The first thing that probably comes to your mind, is to define defaultProps with type TodoItemProps. But when we do this, we need to define a value for the required property text too. So the best way would be a type that makes all optional properties required and vise versa. Sadly there is no solution in TypeScript, that would do this for us. But as a compromise there is a type mapper, that makes all of our properties optional, so we don’t need to define a value for the property text. TypeScript has a few of these type mappers, but the mapper we want here, is called Partial. Partial takes all properties from the given type, and makes them optional.

type Partial<T> = { [P in keyof T]?: T[P] }

With the defaultProps as a Partial type of TodoItemProps, we can now only set values for the properties, we want default values for.

How can we achieve the same for functional components? First we want to define the shape of our properties.

Defining properties’ shape

interface TodoItemProps {
done: boolean;
text: string;
}
const TodoItem = ({done, text}: TodoItemProps) => {
return (...);
}

As we can see, the functional component takes the properties as a function argument. So all we have to do is to set the type of the parameter props to our TodoItemProps type. As functional components are stateless, we don’t have to provide an empty shape for the state, because there is no state.

Defining default properties

To define default properties, we make done optional by appending ? like before. To define the default value for the property done, we now have two options.

ES6 Way: Default parameter value

The ES6 way would be to define the default value as a default value for the function parameter done.

interface TodoItemProps { 
done?: boolean;
text: string;
}
const TodoItem = ({done = false, text}: TodoItemProps) => {
return (...);
};

React Way: As defaultProps

The react way would be to assign the default values to the defaultProps variable, the same way we did in our class before.

interface TodoItemProps {
done?: boolean;
text: string;
}
const TodoItem = (props: TodoItemProps) => {
const { done, text } = props;
...
};
TodoItem.defaultProps: Partial<TodoItemProps> = {
done: false
};

I hope I could show you, how to define the shape of your components props and how to make props default and give them default values.

Follow me on twitter

Originally published at www.danielbischoff.com on February 26, 2018.