在 React TypeScript 中将 useState 键入数组或对象

目录

Type useState as Array of Objects in React TypeScript

  1. 在 React TypeScript 中键入 useState 作为字符串数组
  2. 在 React TypeScript 中键入 useState 作为对象数组
  3. 在 React TypeScript 中键入 useState 作为 OBJECT
  4. 在 React 中键入带有 EMPTY OBJECT 初始值的 useState
  5. 在 React 中键入初始值为 NULL 的 useState 钩子

在 React TypeScript 中输入 useState 作为字符串数组

要在 React 中将钩子键入useState字符串数组,请使用钩子的泛型。

状态变量可以初始化为空数组或字符串数​​组,并且只接受字符串值。

应用程序.tsx
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;

代码示例使用 的类型,但如果您需要存储数字数组或在 React 状态中存储布尔数组,string[]您也可以使用。number[]boolean[]

我们使用泛型来正确键入
useState挂钩,同时使用空数组初始化挂钩

反应使用状态字符串数组打字稿

如果我们没有使用泛型,例如useState<string[]>([])在输入 hook 时,状态变量的类型将是never[],换句话说,一个永远不会包含任何元素的数组。

即使将一个空字符串传递给数组,TypeScript 也能推断出状态变量的类型。

应用程序.tsx
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;

请注意,我们甚至不必使用泛型来键入状态变量。TypeScript 能够根据提供的初始值推断类型。

但是,最佳做法是始终显式键入挂钩,尤其是在处理数组和对象时。 useState

尝试添加不同类型的值会导致错误

如果我们尝试向状态数组添加不同类型的值,我们会得到类型检查错误。

应用程序.tsx
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;

该示例显示了如何尝试将
数字添加到类型为 as 的状态数组string[]导致类型检查器出错。

目录

  1. 在 React TypeScript 中键入 useState 作为对象数组
  2. 在 React TypeScript 中键入 useState 作为 OBJECT
  3. 在 React 中键入带有 EMPTY OBJECT 初始值的 useState
  4. 在 React 中键入初始值为 NULL 的 useState 钩子

在 React TypeScript 中键入 useState 作为对象数组

useState在 React 中将钩子类型化为对象数组,请使用钩子的泛型。

状态变量可以初始化为一个空数组,并且只接受指定类型的对象。

应用程序.tsx
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;

我们使用泛型来正确键入
useState挂钩,同时使用空数组初始化挂钩。

反应打字稿使用状态对象数组

如果我们没有使用泛型,例如
useState<{salary: number; name: string}[]>([])在输入 hook 时,状态变量的类型将是never[],换句话说,一个永远不会包含任何元素的数组。

使用类型别名或接口将 useState 键入为对象数组

如果对挂钩的调用
变得繁忙,
您还可以使用
类型别名
接口useState

应用程序.tsx
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;

我们将对象类型提取到类型别名中,并将其用作Employee[]类型挂钩useState

添加不同类型的值会导致错误

如果我们尝试向状态数组添加不同类型的值,我们会得到类型检查错误。

应用程序.tsx
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;

该示例显示了如何尝试将字符串添加到类型为 as 的状态数组
Employee[]导致类型检查器出错。

目录

  1. 在 React TypeScript 中键入 useState 作为 OBJECT
  2. 在 React 中键入带有 EMPTY OBJECT 初始值的 useState
  3. 在 React 中键入初始值为 NULL 的 useState 钩子

在 React TypeScript 中将 useState 作为对象键入

使用钩子的泛型将useState钩子类型化为 React 中的对象。状态变量将只接受指定类型的键值对。

应用程序.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;

我们使用泛型来正确键入
useState钩子,同时使用对象初始化钩子。

将对象的属性标记为可选

有时您可能不想为对象的所有属性设置初始值。在这种情况下,您可以
将属性标记为可选

应用程序.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;

我们使用问号将该salary属性标记为
可选

该属性可以存储一个undefined值或一个类型的值number

这就是为什么我们在初始化状态对象时不需要提供它。

当提供所有初始值时,TypeScript 推断类型

如果您为对象的所有属性提供初始值,TypeScript 将能够推断出状态变量的类型。

应用程序.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;

我们为对象的所有属性传递了初始值,这使 TypeScript 能够employee正确键入变量。

但是,最佳做法是始终显式键入挂钩,尤其是在处理数组和对象时。 useState

当你事先不知道所有的属性时

在某些情况下,您可能事先不知道要在对象上设置的所有属性。

应用程序.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;

{[key: string]: any}语法是
TypeScript 中的
索引签名,当我们事先不知道类型属性的所有名称和值的形状时使用。

示例中的索引签名意味着当一个对象被索引为 a 时,它将返回一个类型的值。 stringany

当您事先不知道对象的所有属性时,可以使用这种方法。

将对象属性设置为多种类型之一

如果要将对象属性设置为多种类型之一,请使用联合。

应用程序.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;

我们使用联合
salary属性设置为stringor类型number

我还写了一篇关于
定义具有多种类型的数组的教程。

将类型提取useState到类型别名或接口中

如果你的useState钩子很忙,将你传递给泛型的类型提取到类型别名
接口中。

应用程序.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;

语法useState<Employee>更容易阅读,尤其是在处理大对象时。

目录

  1. 在 React 中键入带有 EMPTY OBJECT 初始值的 useState
  2. 在 React 中键入初始值为 NULL 的 useState 钩子

在 React (TS) 中键入空对象初始值的 useState

您还可以使用useState挂钩的泛型来为它键入一个空对象的初始值。

应用程序.tsx
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;

{[key: string]: any}语法是
TypeScript 中的
索引签名,当我们事先不知道类型属性的所有名称和值的形状时使用。

示例中的索引签名意味着当对象被索引为 a 时string,它将返回一个any类型的值。

当您事先不知道对象的所有属性时,可以使用这种方法。

覆盖特定对象属性的类型

您可能会尝试使用索引签名覆盖特定属性的类型。

应用程序.tsx
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;

我们将agetasks属性标记为
可选
,并为它们指定了确切的类型。

可选属性可以有一个
undefined值,也可以是指定的类型。

这就是为什么我们仍然能够将
状态对象初始化为空

但是,为我们事先知道的属性提供类型是有用的,因为 和age属性tasks只能设置为指定的类型。

使用联合类型

如果对象的某些属性可以是多种类型,请使用联合。

应用程序.tsx
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;

我们使用
联合
age属性设置为numberor类型string

您可以重复此过程以根据需要包含尽可能多的类型。

在 React 中键入初始值为 NULL 的 useState 钩子

使用钩子的泛型来为useState钩子键入一个null初始值。

应用程序.tsx
import {useState} from 'react'; const App = () => { // 👇️ with objects const [state, setState] = useState<{ first: string | null; last: string | null; }>({first: null, last: null}); // 👇️ with other types const [name, setName] = useState<string | null>(null); return ( <div> <h2>Name: {name}</h2> <button onClick={() => setName('James Doe')}>Set name</button> <h2>First: {state.first}</h2> <h2>Last: {state.last}</h2> <button onClick={() => setState({first: 'Bob', last: 'Ross'})}> Set state </button> </div> ); }; export default App;

我们使用泛型来正确键入
useStatenull挂钩,同时将其作为初始值传递。

用 null 反应 usestate

如果我们没有使用泛型,例如,useState<string | null>(null)当键入钩子时,状态变量的类型将是null并且我们将无法在不得到类型检查错误的情况下更改它的值。

选择一个更合适的初始值

有时您可以使用更合适的初始值。

例如,如果你正在处理字符串,那么最好设置一个空字符串作为初始值。

应用程序.tsx
// 👇️ const name: string const [name, setName] = useState('');

然后 TypeScript 将能够推断出状态变量的类型,我们甚至不必使用泛型来对其进行类型化。

状态变量可能是null

用作初始值时要记住的一件事null是状态变量的类型是指定类型 OR null

null这意味着在访问任何内置方法之前,您必须检查并确保状态变量的类型不是。

应用程序.tsx
// 👇️ const name: string | null const [name, setName] = useState<string | null>(null); // 👉️ name is string or null here if (name != null) { // 👉️ TypeScript knows that name is string here console.log(name.toUpperCase()); } console.log(name?.toUpperCase());

第一个示例展示了如何在调用内置方法之前if使用充当类型保护的语句来绕过
可能为 null 的值。

一旦我们进入这个if块,TypeScript 就知道变量的类型是,所以我们可以调用它的方法。 namestringtoUpperCase()

第二个示例使用
可选的链接 (?.)null运算符在引用为空值(或)时短路undefined

换句话说,如果name变量存储一个null值,可选链 (?.) 运算符将短路返回undefined,而不是尝试访问值toUpperCase()上的方法null并导致运行时错误。

我还写了一篇关于
如何将 create-react-app 与 TypeScript 结合使用的详细指南。