TypeScript cheatsheet with React
Table of Contents
🙋🏻♀️Introduction
⚛️How to Use TypeScript with React?
🪞Types for React component props
🪢How to type props with TypeScript?
🪡How to type functional component prop?
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
tosrc/index.tsx
) and restart your development server! - If
allowJs
flag in the tsconfig.js file is set totrue
, 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,
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.PropsWithChildren
type takes your component prop and returns a union type with the children
prop 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. 😃