如何让TypeScript推断React Hook传入defaultValue时返回值非undefined?
React Hook的TypeScript类型推断优化:根据defaultValue自动推导返回值是否可选
我实现了一个请求远程API的React Hook,默认在API请求过程中返回值为undefined。现在新增了defaultValue参数,希望请求过程中返回该默认值。想确认TypeScript能否自动推断:当传入defaultValue时,返回值不会包含undefined;未传入时,返回值允许为undefined?
原实现代码
interface UseMyHookOptions<T> { defaultValue?: T } type UseMyHookReturn<T> = T extends undefined ? [T | undefined, (data: T) => void] : [T, (data: T) => void] function useMyHook<T>(key: string, options: UseMyHookOptions<T> = {}): UseMyHookReturn<T>{ const defaultValue = options?.defaultValue const remoteValue = useFetch<T>(`/api/${key}`) const setValue = (value: T) => { console.log(value) } const value = remoteValue ?? defaultValue; return [value, setValue] as UseMyHookReturn<T>; }
预期行为
// `foo`类型:期望是 string(传入了defaultValue) // 实际得到:string const [foo, setFoo] = useMyHook<string>('foo', { defaultValue: 'something' }); // `bar`类型:期望是 string | undefined(未传入defaultValue) // 实际得到:string const [bar, setBar] = useMyHook<string>('bar');
解决方案:用函数重载或拆分泛型参数修复类型推断
原代码的核心问题是:泛型T同时承担了远程返回值类型和默认值类型的角色,导致未传入defaultValue时,TypeScript无法区分T是否包含undefined的情况。以下两种方案可以解决这个问题:
方案1:函数重载(最直观)
通过定义两个函数重载,分别匹配「传入defaultValue」和「未传入defaultValue」的场景:
// 重载1:传入明确的defaultValue时,返回值无undefined function useMyHook<T>(key: string, options: { defaultValue: T }): [T, (data: T) => void]; // 重载2:未传入或传入undefined的defaultValue时,返回值允许undefined function useMyHook<T>(key: string, options?: { defaultValue?: T }): [T | undefined, (data: T) => void]; // 实际实现逻辑 function useMyHook<T>(key: string, options: { defaultValue?: T } = {}) { const defaultValue = options.defaultValue; const remoteValue = useFetch<T>(`/api/${key}`); const setValue = (value: T) => { console.log(value); }; const value = remoteValue ?? defaultValue; return [value, setValue] as const; }
方案2:拆分泛型参数
将远程返回值类型和默认值类型拆分为两个独立的泛型参数,通过条件类型判断默认值是否存在:
interface UseMyHookOptions<D> { defaultValue?: D; } // 根据默认值类型D是否为undefined,决定返回值是否包含undefined type UseMyHookReturn<T, D> = D extends undefined ? [T | undefined, (data: T) => void] : [T, (data: T) => void]; function useMyHook<T, D = undefined>( key: string, options: UseMyHookOptions<D> = {} ): UseMyHookReturn<T, D> { const defaultValue = options.defaultValue; const remoteValue = useFetch<T>(`/api/${key}`); const setValue = (value: T) => { console.log(value); }; const value = remoteValue ?? defaultValue; return [value, setValue] as UseMyHookReturn<T, D>; }
两种方案都能实现预期的类型推断:
- 传入
defaultValue时,返回值的第一个元素类型不会包含undefined - 未传入
defaultValue时,返回值的第一个元素类型为T | undefined
内容的提问来源于stack exchange,提问作者Programmeur
相关产品推荐
相关产品推荐

