TypeScript React hooks 类型怎么写

如果你用 React Hooks 写功能时,经常会碰到这样的场景:明明写了个自定义 Hooks,调用时却弹出“类型不匹配”的警告;或者传给组件的参数,编辑器总提示“缺少必要的类型”。其实用 TypeScript 给 React Hooks 加类型,并没有想象中复杂,只要从最基础的几个 Hooks 入手,掌握核心写法就够了。

基础 Hooks 的类型写法

先从最常用的几个原生 Hooks 开始,它们的类型大多可以通过 TypeScript 自动推断,少数需要手动指定。

TypeScript React hooks 类型怎么写

useState:不用刻意写类型,特殊情况才补充

useState 是最常用的 Hooks,大部分时候不用手动声明类型,TypeScript 能自动“猜”出来。比如写一个计数器:

`jsx

import { useState } from 'react';

function Counter() {

// TypeScript 自动知道 count 是 number,setCount 只能传 number

const [count, setCount] = useState(0);

return <button => setCount(count + 1)}>点击{count}</button>;

}

`

但如果 useState 初始值是 null 或者空数组,就需要手动指定类型。比如要存一个用户信息,初始值是空:

`jsx

type User = { name: string; age: number };

function UserInfo() {

// 告诉 TypeScript,user 要么是 User 类型,要么是 null

const [user, setUser] = useState<User | null>(null);

// 后面接口返回数据后,setUser({ name: "张三", age: 25 }) 就不会报错

}

`

如果不写类型,直接写 useState(null),TypeScript 会默认 user 是 never 类型,后面传数据就会提示错误。

useEffect:不用写类型,处理函数的依赖就好

useEffect 本身不需要写类型,只要注意依赖数组的类型匹配就行。比如监听 count 变化:

`jsx

import { useState, useEffect } from 'react';

function Logger() {

const [count, setCount] = useState(0);

// 依赖数组里的元素类型,必须和 useState 里的类型一致

useEffect(() => {

console.log(count 变成了${count});

}, [count]);

}

`

如果依赖里混了不匹配的类型,比如把字符串放进了只存 number 的依赖数组,编辑器会直接标红提示,不用额外写类型。

useRef:按需指定类型

useRef 有两个常用场景:存 DOM 元素、存可变值。存 DOM 时需要手动指定元素类型,比如拿 input 输入框:

`jsx

import { useRef } from 'react';

function InputFocus() {

// 告诉 TypeScript,这个 ref 只能绑定 HTMLInputElement 类型的 DOM

const inputRef = useRef<HTMLInputElement>(null);

const focusInput = () => {

inputRef.current?.focus(); // 加 ?. 是因为初始值是 null,避免报错

};

return (

<>

<input ref={inputRef} />

<button>

</>

);

}

`

如果不用类型,直接写 useRef(null),inputRef.current 就会是 never,无法调用 focus 方法,编辑器会报错。

自定义 Hooks 的类型写法

写自定义 Hooks 时,需要明确它的入参和返回值类型,这样别人用的时候才不会出错。比如写一个存本地存储的自定义 Hooks:

`jsx

import { useState } from 'react';

// 入参 key 是 string,默认值 value 可以是任意类型,但返回值必须和初始值类型一致

function useLocalStorage<T>(key: string, defaultValue: T): [T, (value: T) => void] {

const [storedValue, setStoredValue] = useState<T>(() => {

try {

const item = window.localStorage.getItem(key);

return item ? JSON.parse(item) : defaultValue;

} catch {

return defaultValue;

}

});

const setValue = (value: T) => {

setStoredValue(value);

window.localStorage.setItem(key, JSON.stringify(value));

};

return [storedValue, setValue];

}

`

这里的 <T> 是泛型,相当于“占位的类型”,调用时自动匹配实际类型:

`jsx

function App() {

// 自动匹配成 string 类型,name 只能是字符串

const [name, setName] = useLocalStorage('username', '');

// 自动匹配成 number 类型,age 只能是数字

const [age, setAge] = useLocalStorage('age', 18);

}

`

如果不写类型,比如把 age 设成字符串,编辑器会直接提示类型不匹配。

注意避坑点

最后提两个容易踩的小坑:一是不要给不需要写的 Hooks 硬加类型,比如 useState 能自动推断的就不用写,写多了反而冗余;二是用泛型时要明确 T 的范围,比如 useLocalStorage 里的 T 可以加限制,避免存函数这类不能转 JSON 的值。

掌握这些写法,用 TypeScript 写 React Hooks 就很少会遇到类型报错的问题,既能享受类型安全的好处,又不用写多余的代码。

上一篇 JavaScript Promise 链式调用怎么写