TypeScript泛型类类型参数未按预期推断,如何修正?
TypeScript泛型类型推断异常问题及解决
问题场景
使用(keyof TFoo)[]接收TFoo键数组时,若泛型采用<TFoo, TBar extends Bar<TFoo>>模式,会出现类型推断不一致的情况:
fooBarBad函数中,第一个参数bar能正确推断TBar为Bar<{ a: number, b: number }>- 但第二个参数
fooKeys却将TFoo推断为{ a: any, c: any },导致传入['a', 'c']未触发预期的类型错误 - 期望两个参数共用同一
TFoo推断结果,像fooBar函数那样能正确校验键的合法性
问题示例代码
class Bar<TFoo> { constructor(foo: TFoo) {} } function fooBarBad<TFoo, TBar extends Bar<TFoo>>(bar: TBar, fooKeys: (keyof TFoo)[]) {} // 无错误,但预期应该报错 - fooKeys被推断为('a' | 'c')[] fooBarBad(new Bar({ a: 1, b: 2 }), ['a', 'c']);
预期行为的简单示例
function fooBar<TFoo>(foo: TFoo, fooKeys: (keyof TFoo)[]) {} // 如预期报错 - 'c'不是TFoo的键 fooBar({ a: 1, b: 2 }, ['a', 'c']);
解决方案
方案1:简化泛型参数,直接约束bar类型
只保留TFoo作为泛型参数,将bar的类型直接设为Bar<TFoo>,让TypeScript优先从bar推断TFoo,再用该类型约束fooKeys:
class Bar<TFoo> { constructor(foo: TFoo) {} } function fooBarFixed<TFoo>(bar: Bar<TFoo>, fooKeys: (keyof TFoo)[]) {} // 如预期报错:类型'"c"'不能赋值给类型'"a" | "b"' fooBarFixed(new Bar({ a: 1, b: 2 }), ['a', 'c']);
方案2:用infer提取TBar中的TFoo(适用于需保留TBar泛型的场景)
若必须保留TBar泛型,可通过条件类型的infer关键字从TBar中提取对应的TFoo,避免独立推断TFoo:
class Bar<TFoo> { constructor(foo: TFoo) {} } function fooBarFixed<TBar extends Bar<any>>( bar: TBar, fooKeys: TBar extends Bar<infer TFoo> ? (keyof TFoo)[] : never ) {} // 如预期报错:类型'"c"'不能赋值给类型'"a" | "b"' fooBarFixed(new Bar({ a: 1, b: 2 }), ['a', 'c']);
方案3:设置泛型参数默认值引导推断
给TFoo设置默认值,引导TypeScript优先从bar参数推断TFoo,而非从fooKeys反向推断:
class Bar<TFoo> { constructor(foo: TFoo) {} } function fooBarBad<TFoo = never, TBar extends Bar<TFoo>>(bar: TBar, fooKeys: (keyof TFoo)[]) {} // 如预期报错:类型'"c"'不能赋值给类型'"a" | "b"' fooBarBad(new Bar({ a: 1, b: 2 }), ['a', 'c']);
原理说明
原问题中,TypeScript会尝试从两个参数同时推断TFoo,当fooKeys传入['a','c']时,它会放宽TFoo的推断结果来匹配两个参数的约束,导致类型校验失效。上述方案通过强制TFoo仅从bar参数推断或从TBar中提取TFoo,避免了双向推断带来的类型放宽问题。
内容的提问来源于stack exchange,提问作者reads0520
相关产品推荐
相关产品推荐

