TypeScript cheatsheet with React

Bhakti Chevli
DhiWise
Published in
9 min readApr 5, 2023

Table of Contents

🙋🏻‍♀️Introduction

⚛️How to Use TypeScript with React?

🪞Types for React component props

🛑ReactNode Vs ReactElement

🏷Type Vs Interface

🪢How to type props with TypeScript?

🪡How to type functional component prop?

React Hooks with TypeScript

Introduction

React is a popular JavaScript library for building user interfaces. It is known for its simplicity, performance, and flexibility. However, JavaScript is a dynamically typed language, which means that errors can occur at runtime. This is where TypeScript comes into the picture.

TypeScript adds static typing to JavaScript, which helps catch errors before they occur. That is, TypeScript does compile time checking. This can improve the quality and maintainability of React applications. Here’s a quick walk-through of its usage.

For code compilation, TypeScript looks for tsconfig.json in the root folder for instructions. It loads and uses the configured settings to build the application.

How to Use TypeScript with React?

  • To add TypeScript to an existing project, first install it:

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

or

yarn add typescript @types/node @types/react @types/react-dom @types/jest

  • Generate a tsconfig.json file manually or with a terminal command, then fill it with my example of the file.

tsc --init

{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noImplicitAny": false,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "./src/"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
  • Next, rename any file to be a TypeScript file (e.g. src/index.js to src/index.tsx) and restart your development server!
  • If allowJs flag in the tsconfig.js file is set to true, it will also allow the usage of .js files. By default, TypeScript only supports .ts files.
  • In React projects, with Vite as a bundler, .tsx files are supported by default. Whereas, in other bundlers like webpack, you need to configure it, in order to enable using .tsx extension. For instance,
Configuring webpack.config.js
configuring webpack.config.js

For further setup, change the file extension to .ts or .tsx as per the requirement.

  • Use .ts while renaming when you are creating functions, classes, reducers, etc. that do not require the use of JSX syntax and elements, whereas the .tsx file extension is used when you create a React component and use JSX elements and syntax.
  • Type errors will show up in the same console as the build one. You’ll have to fix these type errors before you continue development or build your project.

Types for React component props

Props can basically be of primitive data types like string, number, and boolean.

type Person = {
// primitive types
name: string,
age: number,
isUser: boolean
}

To create an array of one type, use an array literal notation ([]) after the type.

type Person = {
name: string[], // an array of string
age: number,
isUser: boolean
}

To get an exact value match for the props, make use of Literals. React will throw an error if the prop value does not match the defined literal value.

type Props = {
type: "old" | "new" | "latest",
part: 1 | 2 | 3
}

To type an object prop,

type Props = {
person: {
name: string,
age: number,
isUser: boolean
}
}

To type children prop, we can use ReactNode.

The React.PropsWithChildrentype takes your component prop and returns a union type with the childrenprop appropriately typed.

type PropsWithChildren<P> = P & { children?: ReactNode };

To type React’s function component, TypeScript has React.FunctionComponent or React.FC to use a shorter version of it.

type Person = {
name : string
}
const App:React.FC<Person> = ({name}) => {
return (
<h1>{name}</h1>
)
}

Good news is, as TypeScript has inferred type features, typing React function components every time is not required.

Typing form elements is pretty simple with TypeScript.

The handleChange function takes an event of type React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, which means it can handle events from both input and textarea elements.

ReactNode Vs ReactElement

React Element

A React Element is a description of a component that can be rendered to the screen. It is a plain JavaScript object that represents a DOM element or a component.

React Elements are created using the JSX syntax, which is a syntax extension for JavaScript that allows you to write HTML-like code in your JavaScript files. For example, the following JSX code creates a React Element that represents a heading:

const heading = <h1>Hello, World!</h1>;

React Node

A React Node, on the other hand, is a more general term that refers to any object that can be rendered in the React DOM.

This includes not only React Elements but also strings, numbers, booleans, and other JavaScript objects. For example, the following code creates a React Node that is a string:

const message = "Hello, World!";
const node = <div>{message}</div>;

Type Vs Interface

Type

type defines the structure of objects or complex data types that we use in our code.

TypeScript has any datatype that’s a superset of all the types. It further has a built-in and user-defined datatype.

Interface

In TypeScript, an interface gives details about an object's shape. The TypeScript compiler can use them to give information about the object property names and the datatypes that their values can contain.

Use case of type aliases Vs interface:

type and interface just perform equally well.

If you wish to add some properties/methods/events after defining a standard structure, using an interface is preferable. Using type to do so, throws an error; once it’s defined. type cannot be changed outside of its definition. Declaration merging won’t work in type. On the other hand, interface is extendible(mergeable).

With type and interface, you can extend some features/properties/methods of the existing structure. An interface is preferable as it makes it easy and manageable further to do so.

type is most widely used unless you need a specific feature of an interface for exposing it publicly. Moreover, type is also used to create tuples, unions, and intersections. Also, interface works great with classes. An interface can extend a type and a type can intersect an interface unless the following scenarios arise:

An interface can’t extend something where it does not know the incoming type. If you lean heavily on “union” for a lot of your code, then opt for using type over using an interface.

You can create type aliases and implement them with the class too. A class cannot implement a union type.

How to type props with TypeScript?

Props in React are used to store and pass data from a parent component to a child component. Props play a vital role when it comes to building real-world applications. Let’s see typing some of the props.

Its syntax is pretty simple. You need to add: and the object literal notation {} next to the destructuring assignment of the children prop at the component declaration. Also, you can create a Type alias for the prop. You can even make a prop optional by adding? after the prop name.

const app = ({ title }:{ title : string }) => (
<h1>{title}</h1>
)

Typing Event Props:

We have a variety of categorized events. Let’s take an example of typing a click event as a component prop. React props can also receive functions such as onClick and onChange, so you may need to type function props. The button component accepts the click event as the prop and passes it to the HTML Button element.

Typing Functional Component Props:

TypeScript has React.FunctionComponent or you can say React.FC to type React Function Components.

This approach is used, to access an additional set of properties like propTypes, defaultProps, contextTypes, etc, and also let TypeScript know that it’s a React component.

Typing Hooks

Hooks are supported in [@types/react from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031). One doesn’t really need to Type the hook every time. Most of the time, this will be taken care of by Type Inference for simple values.

useState hook

However, to type hooks, explicitly define the type and use the union type:

const [person, setPerson] = useState<Person | null>(null);
//to use later
setPerson(newPerson);

You can also use type assertions if a state is initialized soon after set up and always has a value after:

In the case where the state is initialized soon after the setup and will be assigned a value after, hooks can be typed in the following ways:

const [person, setPerson] = useState<Person>({} as Person);
// to use later
setPerson(newPerson);

useCallback hook

Typing useCallbacks is fun. You can type useCallback hook just like you type functions.

const myCallback = useCallback(
(name: string, age: number) => {
console.log(name, age)
return { status: true }
},
[...],
);

Note that the function signature may vary depending on the version of React.

useReducer hook

Discriminated Unions fit perfectly well with Reducers. Also, they infer the return type if you don’t specify it.

import { useReducer } from "react";
const initialState = { count: 0 };type ACTIONTYPE =
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string };
function reducer(state: typeof initialState, action: ACTIONTYPE) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "decrement", payload: "5" })}>
-
</button>
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
+
</button>
</>
);
}

useRef hook

In TypeScript, depending on whether your type parameter completely covers the original value or not, useRef provides a reference that is either read-only or changeable.

function Myfunction() {

const divRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
if (!divRef.current) throw Error("divRef is not assigned");
doSomethingWith(divRef.current);
});
return <div ref={divRef}>etc</div>;
}

Provide only the element type as an argument, and use null as an initial value. In this case, the returned reference will have a read-only .current that is managed by React. TypeScript expects you to give this ref to an element's ref prop.

forwardRef hook

forwardRef is a higher-order component that allows a component to pass a ref to one of its children.

When using forwardRef, it’s important to define the types of the props that are being passed to the component.

About Me:

I’m a Software Developer and Dev Advocate at DhiWise👩‍💻.
DhiWise lets you build React and Flutter Apps at blazing-fast speed without compromising on code quality and developer experience. Check out the platform for free to know more and don’t forget to let us know your experience on our Discord🤘

You can find me on LinkedIn and Twitter to stay in touch with the latest tech updates about DhiWise. 😃

--

--