React Props and TypeScript

Cristian Cedacero
4 min readOct 1, 2021

In this post, we’ll be covering using React with TypeScript. If you need a quick refresher on TypeScript, I recommend you read my previous article here.

One of the highlights of using TypeScript is that it allows us to specify the props our components expect, making it easier for developers to understand the code and thus helping them debug faster. In this post, we’ll go over some of the most common use cases for TypeScript with React.

Defining Component Props

type Props {
name: string
hometown: string
favoriteRestaurant?: string
}
const ContactForm= ({ name, hometown, favoriteRestaurant }: Props) => (
<>
<p>
Name: {name}
Hometown: {hometown}.
{favoriteRestaurant ?
`favoriteRestaurant: ${favoriteRestaurant}`} : null }
</p>
</>
);

When working with React functional components, to define strongly-typed props, we follow the same approach that we use for defining types for regular functions. We can declare them in-line, use type aliases, or an interface. In our example above, we’re using a type alias and we’re optionally conditionally rendering the favorite restaurant field if it’s present.

Normal declaration of default props 
const ContactForm= ({ name, hometown, favoriteRestaurant = ' Alidoro' }: Props)
React's alternative to default props
ContactForm.defaultProps = {
favoriteRestaurant: 'Alidoro'
};

Often times, we may need to declare default values in our props. Yet again, this is similar to declaring default values in regular functions. Above, we can observer two ways we can declare default props for our components.

React.FCconst ContactForm: React.FC<Props>  = ({ name, hometown, favoriteRestaurant }) => {
return (
<>
<p>
Hello, {name}
Hometown, {hometown}
favoriteRestaurant, {favoriteRestaurant}
</p>
}
</>
);

On arrow functions, if we’d like to define a single use case type, we can take advantage of the built in generic type, React Function Component. Using this generic type allows us to simply pass our props to the function parameter without defining our own types separately. We have our props parameter in angle brackets and pass our values inside the function parameters .You’ll probably realize that this is the same way we’d use any generic type we’ve created.

Render Props

type ContactForm {
name: string
hometown: string
}
type EventList= {
contact: ContactForm
greeting: string
vip?: boolean
renderVip?: (vip: greeting) => React.ReactNode;
}
const Greet = ({
contact,
greeting,
vip,
renderVip
}: EventList) => (
<>
<p>
Welcome, {contact.name} from {contact.hometown}
</p>
{vip ? (renderVip ? renderVip(greeting) : <p>{greeting}</p>)}
<>
);
Passing the props:
<Greet
contact={{ name: "Toby", hometown: NYC }},
vip: true,
greeting: "Thank you for you for your support"
renderVip={greeting => <h5> You are awesome!: {greeting}</h5>}
/>

If working on a component that uses render props to control how a component will be rendered, we can use type our render props. The process is identical to defining regular types, except that our type will accept an optional function that will render a React.ReactNode element which will be used to render our react elements. In our example above, we have a functional component that allows an optional render function that has a special greeting just for vip guests.

Handling Component State

Inferred State on primitive values
const [name, setName] = React.useState('Toby'); //string
const [age, setAge] = React.useState(7); //number
Defining state
const [name, setName] = React.useState<string | null> (null);
const [age, setAge] = React.useState<number | null> (7);
type ContactForm {
name: string
age: number
favoriteRestaurant?: number
}
const [form, setForm] = React.useState<ContactForm | null> (null);

When declaring state, if we’re working with JavaScript’s primitive values, TypeScript is able to infer the state type based on the value initialized. If we’d like to declare our state explicitly, we can use the React.useState generic function and pass it our type alias, interface, or inline value. In our examples above, you can see how declaring state is just like working with any other generic type.

Event Handlers

Intellisense event type suggestion
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) =>
setQuery(event.currentTarget.value);

When working with React, we often find ourselves working with change events and carrying out actions with their value. TypeScript is really friendly in that if we’re using inline events, it will automatically infer the event type for that event. If we need more logic or would just like to create a separate function to handle the event, we can use use the inferred event type as our type to create a strongly typed function to handle the change. In our example above, we have a handle change function that takes a react change event to set a new query.

There’s still more to cover as we haven’t covered creating using typed refs, context, or useReducer, with component state but those topics are a bit more complex and will require a demo app in order for them to make sense. I’ll be covering them in the next blog post soon! Hope this brief introduction helped you learn a thing or two about using react with TypeScript. Stay tuned for the next post!

--

--