Last updated: Feb 29, 2024
Reading timeยท9 min
To type the useState
hook as an array of strings in React, use the hook's
generic.
The state variable can be initialized to an empty array or an array of strings and will only accept string values.
import React from 'react'; import {useState} from 'react'; const App = () => { // ๐๏ธ const names: string[] const [names, setNames] = useState<string[]>([]); return ( <div> <button onClick={() => setNames(prevNames => [...prevNames, 'Bob'])}> Add name </button> {names.map((element, index) => { return ( <div key={index}> <h2>{element}</h2> </div> ); })} </div> ); }; export default App;
The code sample uses a type of string[]
, but you could also use number[]
if
you need to store an array of numbers or boolean[]
to store an array of
booleans in your React state.
We used a generic to type the useState hook correctly while initializing the hook with an empty array.
Had we not used the generic, e.g. useState<string[]>([])
when typing the hook,
the type of the state variable would be never[]
, in other words, an array that
will never contain any
elements.
If you pass even a single empty string to the array, TypeScript will be able to infer the type of the state variable.
import React from 'react'; import {useState} from 'react'; const App = () => { // ๐๏ธ const names: string[] const [names, setNames] = useState(['']); return ( <div> <button onClick={() => setNames(prevNames => [...prevNames, 'Bob'])}> Add name </button> {names.map((element, index) => { return ( <div key={index}> <h2>{element}</h2> </div> ); })} </div> ); }; export default App;
Notice that we didn't even have to use a generic to type the state variable. TypeScript was able to infer the type based on the provided initial value.
useState
hook, especially when working with arrays and objects.If we try to add a value of a different type to the state array, we would get a type checking error.
import React from 'react'; import {useState} from 'react'; const App = () => { // ๐๏ธ const names: string[] const [names, setNames] = useState<string[]>([]); // โ๏ธ Argument of type '(prevNames: string[]) => (string | number)[]' is not // assignable to parameter of type 'SetStateAction<string[]>'. setNames(prevNames => [...prevNames, 1000]); return ( <div> <button onClick={() => setNames(prevNames => [...prevNames, 'Bob'])}> Add name </button> {names.map((element, index) => { return ( <div key={index}> <h2>{element.toUpperCase()}</h2> </div> ); })} </div> ); }; export default App;
The example shows how trying to
add a number to a state array that is typed
as string[]
causes the type checker to error out.
To type the useState
hook as an array of objects in React, use the hook's
generic.
The state variable can be initialized to an empty array and will only accept objects of the specified type.
import React from 'react'; import {useState} from 'react'; const App = () => { // ๐๏ธ const employees: {salary: number;name: string;}[] const [employees, setEmployees] = useState<{salary: number; name: string}[]>( [], ); return ( <div> <button onClick={() => setEmployees(prevEmployees => [ ...prevEmployees, {salary: 100, name: 'Bob'}, ]) } > Add employee </button> {employees.map((employee, index) => { return ( <div key={index}> <h2> salary: {employee.salary} / name: {employee.name} </h2> </div> ); })} </div> ); }; export default App;
We used a generic to type the useState hook correctly while initializing the hook with an empty array.
Had we not used the generic, e.g.
useState<{salary: number; name: string}[]>([])
when typing the hook, the type
of the state variable would be never[]
, in other words, an array that will
never contain any elements.
You can also use a type alias or an
interface if the call to the
useState
hook gets busy.
import React from 'react'; import {useState} from 'react'; type Employee = { salary: number; name: string; }; const App = () => { // ๐๏ธ const employees: Employee[] const [employees, setEmployees] = useState<Employee[]>([]); return ( <div> <button onClick={() => setEmployees(prevEmployees => [ ...prevEmployees, {salary: 100, name: 'Bob'}, ]) } > Add employee </button> {employees.map((employee, index) => { return ( <div key={index}> <h2> salary: {employee.salary} / name: {employee.name} </h2> </div> ); })} </div> ); }; export default App;
We extracted the object type into a type alias and used it as Employee[]
to
type the useState
hook.
If we try to add a value of a different type to the state array, we would get a type checking error.
import React from 'react'; import {useState} from 'react'; type Employee = { salary: number; name: string; }; const App = () => { // ๐๏ธ const employees: Employee[] const [employees, setEmployees] = useState<Employee[]>([]); // โ๏ธ Argument of type '(prevEmployees: Employee[]) => (string | Employee)[]' is not assignable to parameter of type 'SetStateAction<Employee[]>'. setEmployees(prevEmployees => [...prevEmployees, 'Hello world']); return ( <div> <button onClick={() => setEmployees(prevEmployees => [ ...prevEmployees, {salary: 100, name: 'Bob'}, ]) } > Add employee </button> {employees.map((employee, index) => { return ( <div key={index}> <h2> salary: {employee.salary} / name: {employee.name} </h2> </div> ); })} </div> ); }; export default App;
The example shows how trying to add a string to a state array that is typed as
Employee[]
causes the type checker to error out.
Use the hook's generic to type the useState
hook as an object in React. The
state variable will only accept key-value pairs of the specified type.
import React from 'react'; 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.
import React from 'react'; 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
.
If you provide initial values for all of your object's properties, TypeScript will be able to infer the type of the state variable.
import React from 'react'; 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.
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.
import React from 'react'; 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.
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.
import React from 'react'; 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
.
I've also written a tutorial on defining an array with multiple types.
useState
into a Type Alias or an InterfaceIf your useState
hook gets busy, extract the type you pass to the generic into
a type alias or an
interface.
import React from 'react'; 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.
You can also use the useState
hook's generic to type it with an initial value
of an empty object.
import React from 'react'; import {useEffect, useState} from 'react'; const App = () => { // ๐๏ธ const employee: {[key: string]: any;} const [employee, setEmployee] = useState<{[key: string]: any}>({}); useEffect(() => { setEmployee({ name: 'Alice', 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.
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.
You might try to override the type of a specific property with an index signature.
import React from 'react'; import {useEffect, useState} from 'react'; type Employee = { [key: string]: any; age?: number; tasks?: string[]; }; const App = () => { // ๐๏ธ const employee: {[key: string]: any;} const [employee, setEmployee] = useState<Employee>({}); useEffect(() => { setEmployee({ name: 'Alice', salary: 100, department: 'Dev', tasks: ['dev', 'test', 'ship'], }); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;
We marked the age
and tasks
properties as
optional
and specified exact types for them.
Optional properties can either have an
undefined
value or be of the specified type.
This is why we are still able to initialize the state object as empty.
age
and tasks
properties can only be set to the specified type.If some of the properties of the object can be of multiple types, use a union.
import React from 'react'; import {useEffect, useState} from 'react'; type Employee = { [key: string]: any; // ๐๏ธ age is number OR string age?: number | string; tasks?: string[] | number[]; }; const App = () => { // ๐๏ธ const employee: {[key: string]: any;} const [employee, setEmployee] = useState<Employee>({}); useEffect(() => { setEmployee({ name: 'Alice', salary: 100, department: 'Dev', tasks: ['dev', 'test', 'ship'], }); }, []); return ( <div> <h2>Name: {employee.name}</h2> <h2>Salary: {employee.salary}</h2> </div> ); }; export default App;
We used a
union
to set the age
property to be of type number
or string
.
You can repeat this process to include as many types as necessary.