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

如何实现TypeScript泛型函数参数的严格键类型校验?

如何让TypeScript正确校验Hook函数的参数类型

我编写了一个Hook函数,传入对象的键会用于推导返回类型:

type MyOptions = {
  [key: `${number}${'s'|'ms'}`]: string
}

function useMyHook<O extends MyOptions>(options: O): MyReturnType<O> {
  // ...
}

调用该函数时,TypeScript不会对无效键报错:

useMyHook({
  foo: 'bar'
})

但如果直接定义变量并指定类型,TypeScript会正常识别无效键:

const options: MyOptions = {
  foo: 'bar'
}

请问怎么才能让函数参数也被正确校验?


问题根源

用O extends MyOptions做泛型约束时,TypeScript会基于结构类型兼容性判断:只要传入对象的结构和MyOptions兼容(哪怕包含额外键),就会被允许。因为MyOptions的索引签名只定义了允许的键格式,并没有禁止额外键,所以传入foo这类无效键时,TypeScript会把O推断为{ foo: string } & MyOptions,不会触发报错。

而直接给变量指定MyOptions类型时,TypeScript会执行严格赋值检查,不允许存在索引签名未覆盖的额外键,因此会正常报错。

解决方案

要让函数参数严格符合MyOptions的键格式,需要修改泛型约束,强制O的所有键都匹配${number}${'s'|'ms'}规则,以下是两种可行方案:

方案1:双重约束确保键格式合法

通过MyOptions & Record<keyof O, ${number}${'s'|'ms'}>强制所有键都符合要求:

type MyOptions = {
  [key: `${number}${'s'|'ms'}`]: string
}

type MyReturnType<O> = {
  [K in keyof O]: number
}

function useMyHook<O extends MyOptions & Record<keyof O, `${number}${'s'|'ms'}`>>(options: O): MyReturnType<O> {
  // 示例实现
  return Object.fromEntries(
    Object.entries(options).map(([k, v]) => [k, parseInt(v)])
  ) as MyReturnType<O>
}

// 报错:类型“"foo"”的属性不能赋给类型“`${number}${"s" | "ms"}`”的索引类型
useMyHook({
  foo: 'bar'
})

// 正常通过校验
useMyHook({
  '100s': '100',
  '200ms': '200'
})

方案2:反向键约束(TypeScript 4.9+)

利用keyof MyOptions会被解析为${number}${'s'|'ms'}的特性,直接约束键的类型:

type MyOptions = {
  [key: `${number}${'s'|'ms'}`]: string
}

type MyReturnType<O> = {
  [K in keyof O]: number
}

function useMyHook<O extends Record<keyof O, string> & { [K in keyof O]: K extends keyof MyOptions ? string : never }>(options: O): MyReturnType<O> {
  // 示例实现
  return Object.fromEntries(
    Object.entries(options).map(([k, v]) => [k, parseInt(v)])
  ) as MyReturnType<O>
}

// 同样会触发无效键报错
useMyHook({
  foo: 'bar'
})

方案3:直接使用MyOptions作为参数类型(无键类型推导)

如果不需要返回类型精确对应传入的具体键,直接把参数类型设为MyOptions即可,但这样返回类型无法推导具体键名:

function useMyHook(options: MyOptions): MyReturnType<MyOptions> {
  // ...
}

如果需要保留返回类型和传入键的一一对应关系,方案1是更优选择。


内容的提问来源于stack exchange,提问作者Spadar Shut

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:37:16