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

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 就很少会遇到类型报错的问题,既能享受类型安全的好处,又不用写多余的代码。