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

如何让TypeScript对泛型键不一致的参数抛出正确错误?

如何让TypeScript在泛型参数不匹配时正确抛出类型错误?

我定义了一个包含不同版本输入输出类型的接口,随后编写了泛型函数,期望输入和输出对应同一版本的类型,但TypeScript在不显式指定泛型或传入版本键的情况下,错误允许混合不同版本的参数。代码示例如下:

interface Versions {
  v1: {
    input: {
      v1Input: boolean
    }
    output: {
      v1Output: boolean
    }
  }
  v2: {
    input: {
      v2Input: boolean
    }
    output: {
      v2Output: boolean
    }
  }
}

// 这个版本无法正确校验
function io<T extends keyof Versions>(
  input: Versions[T]['input'],
  output: Versions[T]['output'],
) {}

// 这里输入和输出不匹配,应该抛出类型错误,但实际没有
io({ v1Input: true }, { v2Output: true }) 

// 显式指定泛型后会正确报错:
// "对象字面量只能指定已知属性,但 'v2Output' 不在类型 '{ v1Output: true; }' 中。是否想写 'v1Output'?(2561)"
io<'v1'>({v1Input: true }, { v2Output: true }) 

// 这个版本能正确校验(因为传入了版本键)
function io2<T extends keyof Versions>(
  input: Versions[T]['input'],
  output: Versions[T]['output'],
  key: T
) {}

// 正确抛出错误
io2({ v1Input: true }, { v2Output: true }, 'v1')

// 添加第二个泛型也无法解决问题
function io3<T extends keyof Versions, V extends Versions[T]>(
  input: V['input'],
  output: V['output'],
) {}

// 仍然不会抛出错误
io3({ v1Input: true }, { v2Output: true })

问题原因

出现这个问题的核心是TypeScript的泛型类型推断宽松匹配机制:当泛型约束为T extends keyof Versions时,TypeScript会尝试寻找一个满足条件的T。如果传入的参数能分别匹配不同T的子类型(比如输入匹配v1、输出匹配v2),TypeScript会认为存在联合类型T = 'v1' | 'v2'满足约束,因此不会报错。

解决方案

方案1:定义版本输入输出对的联合类型

通过构造所有版本输入输出对的联合类型,强制TypeScript只能匹配其中某一个完整的版本组合,无法拆分参数:

// 生成所有版本的输入输出对联合类型
type IOCombination = {
  [K in keyof Versions]: {
    input: Versions[K]['input'],
    output: Versions[K]['output']
  }
}[keyof Versions];

function io(params: IOCombination) {}

// 现在会正确报错:类型不匹配
io({ input: { v1Input: true }, output: { v2Output: true } })

方案2:用条件类型强化泛型绑定

通过条件类型限制输出类型必须与输入对应的版本严格匹配,避免TypeScript用联合类型兼容拆分参数:

function io<T extends keyof Versions>(
  input: Versions[T]['input'],
  // 用条件类型明确绑定输出与T的关系
  output: T extends keyof Versions ? Versions[T]['output'] : never
) {}

// 正确抛出类型错误
io({ v1Input: true }, { v2Output: true })

方案3:直接约束泛型为版本对象类型

将泛型直接约束为Versions的子类型,让输入输出必须属于同一个具体版本:

function io<T extends Versions[keyof Versions]>(
  input: T['input'],
  output: T['output']
) {}

// 正确抛出类型错误
io({ v1Input: true }, { v2Output: true })

这些方案的核心逻辑都是消除TypeScript用联合类型兼容拆分参数的可能性,强制输入和输出必须属于同一个具体版本,从而实现严格的类型校验。

内容的提问来源于stack exchange,提问作者David Y. Stephenson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 22:17:43