如何实现类型安全的React自定义Hook?解决参数校验问题
自定义Hook类型安全优化方案
问题描述
调用自定义函数createUser时,传入数字而非字符串不会触发类型错误,遗漏必填参数也没有IDE提示。调用代码如下:
const [userState, createUser] = useCreateUser(); ... <div onClick={(): void => { createUser({ email: 'test796420@gmail.com', name: 'test1', password: 'topSecret!', username: 'testuser1', }); }} > createUser </div>
自定义Hook实现代码:
import { useState } from 'react'; interface CreateUserShape { email: string; name: string; password: string; referrersCode: string; username: string; } const useCreateUser = (initialState = {}): any => { const [state, setState] = useState(initialState); const createUser = async (user: CreateUserShape): Promise<void> => { if (user.email && user.password) { const method = 'POST'; const redirect = 'follow'; const headers = new Headers(); const body = JSON.stringify(user); const url = 'https://my-fancy-app.ondigitalocean.app/api/v1/onboarding/createUser'; headers.append('Content-Type', 'application/json'); const res = await fetch(`${url}`, { method, headers, redirect, body, }); const response = await res.json(); setState({ response }); } }; return [state, createUser]; }; export { useCreateUser };
优化方案
核心问题是useCreateUser返回类型设为any,导致TypeScript无法推断createUser的类型;同时initialState默认值为{},让useState失去类型约束。按以下步骤修改:
1. 定义Hook返回值类型
创建类型别名,明确Hook返回的数组结构:
type UseCreateUserReturn = [ { response?: unknown }, // 匹配setState存入的结构 (user: CreateUserShape) => Promise<void> ];
2. 给useCreateUser指定返回类型和参数类型
将useCreateUser的返回类型从any改为上面定义的UseCreateUserReturn,同时给initialState指定类型:
const useCreateUser = (initialState: { response?: unknown } = {}): UseCreateUserReturn => { const [state, setState] = useState<{ response?: unknown }>(initialState); // 原有逻辑不变 return [state, createUser]; };
3. 确保CreateUserShape的必填性
当前CreateUserShape的所有字段都是必填的,调用createUser时遗漏referrersCode会直接触发TS错误。如果某些字段可选,可在字段名后加?,比如referrersCode?: string。
修改后的完整代码
import { useState } from 'react'; interface CreateUserShape { email: string; name: string; password: string; referrersCode: string; username: string; } type UseCreateUserReturn = [ { response?: unknown }, (user: CreateUserShape) => Promise<void> ]; const useCreateUser = (initialState: { response?: unknown } = {}): UseCreateUserReturn => { const [state, setState] = useState<{ response?: unknown }>(initialState); const createUser = async (user: CreateUserShape): Promise<void> => { if (user.email && user.password) { const method = 'POST'; const redirect = 'follow'; const headers = new Headers(); const body = JSON.stringify(user); const url = 'https://my-fancy-app.ondigitalocean.app/api/v1/onboarding/createUser'; headers.append('Content-Type', 'application/json'); const res = await fetch(`${url}`, { method, headers, redirect, body, }); const response = await res.json(); setState({ response }); } }; return [state, createUser]; }; export { useCreateUser };
效果验证
修改后:
- 传入非字符串类型的参数(比如
email: 123)会立即触发TypeScript错误 - 遗漏
CreateUserShape中的必填字段(比如referrersCode)时,IDE会显示红色波浪线提示 - IDE会自动补全
createUser的参数字段,提升开发效率
内容的提问来源于stack exchange,提问作者Bill
相关产品推荐
相关产品推荐

