Add TypeScript To An Existing React Project
Learn how to add TypeScript to your existing React project in a few simple steps.
When you're working in a React app, declaring a type for your React props is likely going to be your most common TypeScript activity.
There's some debate over the best method for typing props. In this article, I'll show you the pros and cons of each method and give you my recommendation.
If you want to learn by doing, check out my free interactive course on React and TypeScript.
Props can be declared using an inline object literal.
tsx
import {ReactNode } from "react";constWrapper = (props : {children ?:ReactNode ;}) => {return <div >{props .children }</div >;};
Prop types can also be destructured, leading to a strange {}: {}
syntax.
tsx
import {ReactNode } from "react";constWrapper = ({children ,}: {children ?:ReactNode ;}) => {return <div >{children }</div >;};
Prop types can also be extracted to a type alias:
tsx
import {ReactNode } from "react";export typeWrapperProps = {children ?:ReactNode ;};constWrapper = (props :WrapperProps ) => {return <div >{props .children }</div >;};
Type aliases should always be exported along with the component - that way you can use them in other files if needed.
Props declared in a type alias can also be destructured:
tsx
constWrapper = ({children }:WrapperProps ) => {return <div >{children }</div >;};
Interfaces are another way to declare props:
tsx
import {ReactNode } from "react";export interfaceWrapperProps {children ?:ReactNode ;}constWrapper = (props :WrapperProps ) => {return <div >{props .children }</div >;};
Just like type aliases, interfaces should always be exported so they can be reused later.
And, just like type aliases, interfaces can be destructured.
tsx
constWrapper = ({children }:WrapperProps ) => {return <div >{children }</div >;};
Now that we've seen all the methods, let's look at all the pros and cons before we make our final decision.
The main benefit of inline object literals is speed. You can type your props quickly and move on.
The issue becomes that inline object literals will, eventually, need to be extracted out to a type.
So if you're writing a component, you should probably use a type alias or interface.
To get over this hurdle, I'd advise writing your own code snippets for type aliases and interfaces.
Here's a code snippet that will work in VSCode for creating your own components.
json
{"component": {"prefix": "comp","body": ["export interface $1Props {"," $2","}","","export const $1 = (props: $1Props) => {"," return $3","}"]}}
Autocompleting to comp
will expand to a component with an interface, both exported.
My opinions on type aliases vs. interfaces are well documented in this article. In short, I prefer the type
in general because of the 'declaration merging' property of the interface
, which can confuse and frustrate beginners.
But there's one particular case where interfaces win over type aliases - that's in creating complex intersections of props.
Let's say you're creating a component that has all the props of input
but needs to add a label
prop. You'll need to extend from the ComponentProps
type helper, described in my article.
It is tempting to use a type
alias:
tsx
import {ComponentProps } from "react";export typeInputProps =ComponentProps <"input"> & {label : string;};export functionInput ({label ,...props }:InputProps ) {return (<label >{label }<input {...props } /></label >);}
But unfortunately, intersections used this way will, on the scale of a large codebase, slow TypeScript down.
Instead, you should use an interface, using interface extends
:
tsx
import {ComponentProps } from "react";export interfaceInputProps extendsComponentProps <"input"> {label : string;}
This advice comes from TypeScript's performance wiki, as well as a PR from Sentry's codebase that sped up their type checking by removing these intersections in favor of interfaces.
So in situations where you're extending from other types, use interfaces.
In general, you should use interface
to declare your props. They're the most performant, and with a code snippet, they're fast to write.
If you've already got props declared using type
or inline object literals, don't worry - this isn't worth a refactor.
But if your codebase starts to feel sluggish in your IDE, check for intersections in your props. If you find any, try to refactor them to interface extends
.
Share this article with your friends
Learn how to add TypeScript to your existing React project in a few simple steps.
Learn the essential TypeScript configuration options and create a concise tsconfig.json file for your projects with this helpful cheatsheet.
Big projects like Svelte and Drizzle are not abandoning TypeScript, despite some recent claims.
Learn different ways to pass a component as a prop in React: passing JSX, using React.ComponentType, and using React.ElementType.
Learn about TypeScript performance and how it affects code type-checking speed, autocomplete, and build times in your editor.
Learn TypeScript in 2023 with this step-by-step guide. Understand the basics, essential types, unions, and narrowing techniques.