Type useState as an Object in React TypeScript

avatar

Borislav Hadzhiev

Last updated: Apr 20, 2022

banner

Photo from Unsplash

Type useState as an Object in React TypeScript #

To type the useState hook as an object in React, use the hook's generic, e.g. const [employee, setEmployee] = useState<{name: string; salary: number}>({name: '',salary: 0}). The state variable will only accept key-value pairs of the specified type.

App.tsx
import {useEffect, useState} from 'react'; const App = () => { // 👇️ const employee: {name: string; salary: number;} const [employee, setEmployee] = useState<{name: string; salary: number}>({ name: '', salary: 0, }); useEffect(() => { setEmployee({name: 'James', salary: 100}); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

We used a generic to type the useState hook correctly while initializing the hook with an object.

Sometimes you might not want to set initial values for all of the object's properties. In this case you can mark the properties as optional.

App.tsx
import {useEffect, useState} from 'react'; const App = () => { // 👇️ mark salary as optional const [employee, setEmployee] = useState<{ name: string; salary?: number }>({ name: '', }); useEffect(() => { setEmployee({name: 'James', salary: 100}); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

We used a question mark to mark the salary property as optional.

The property can either store an undefined value or a value of type number.

This is why we aren't required to provide it when initializing the state object.

If you provide initial values for all of your object's properties, TypeScript will be able to infer the type of the state variable.

App.tsx
import {useEffect, useState} from 'react'; const App = () => { // 👇️ const employee: {name: string;salary: number;} // ✅ typed correctly without a generic const [employee, setEmployee] = useState({ name: '', salary: 0, }); useEffect(() => { setEmployee({name: 'James', salary: 100}); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

We passed initial values for all of the object's properties, which enabled TypeScript to type the employee variable correctly.

However, it is a best practice to always explicitly type the useState hook, especially when working with arrays and objects.

In some cases, you might not know all of the properties you will set on the object in advance.

App.tsx
import {useEffect, useState} from 'react'; const App = () => { // 👇️ flexible object type const [employee, setEmployee] = useState<{[key: string]: any}>({}); useEffect(() => { setEmployee({ name: 'James', salary: 100, department: 'Dev', tasks: ['dev', 'test', 'ship'], }); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

The {[key: string]: any} syntax is an index signature in TypeScript and is used when we don't know all the names of a type's properties and the shape of the values ahead of time.

The index signature in the example means that when an the object is indexed with a string, it will return a value of any type.

You can use this approach when you don't know all of the object's properties in advance.

If you want to set an object property to be one of multiple types, use a union.

App.tsx
import {useEffect, useState} from 'react'; const App = () => { const [employee, setEmployee] = useState<{ name: string; // 👇️ string OR number salary: string | number; }>({ name: '', salary: '', }); useEffect(() => { setEmployee({name: 'James', salary: 100}); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

We used a union to set the salary property to be of type string or number.

If your useState hook gets busy, extract the type you pass to the generic into a type alias or an interface.

App.tsx
import {useEffect, useState} from 'react'; type Employee = { name: string; salary: number; }; const App = () => { // 👇️ const employee: {name: string; salary: number;} const [employee, setEmployee] = useState<Employee>({ name: '', salary: 0, }); useEffect(() => { setEmployee({name: 'James', salary: 100}); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;

The syntax useState<Employee> is much easier to read, especially when dealing with large objects.

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.