You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:47:10