TypeScript泛型函数中如何关联相关参数并修正类型错误
为泛型函数参数添加关联类型约束
我需要给自定义泛型函数的两个参数添加正确的类型约束,要求参数之间存在关联关系——第一个参数是Foo['union']中的字符串字面量,第二个参数必须对应Foo['object']里首字母大写的键对应的对象类型。
最小可复现示例
(注:Foo接口和objectOne对象来自不可修改的第三方库,仅可修改自定义函数func的参数类型)
interface Foo { union: 'one' | 'two' | 'three'; object: { One: {'oneRelatedKey': 'oneRelatedValue'}; Two: {'twoRelatedKey': 'twoRelatedValue'}; Three: {'threeRelatedKey': 'threeRelatedValue'}; }; }; const objectOne = { one: { func: (obj: Foo['object']['One']) => {console.log(obj)} }, two: { func: (obj: Foo['object']['Two']) => {console.log(obj)} }, three: { func: (obj: Foo['object']['Three']) => {console.log(obj)} }, }; // 原写法,存在类型错误 const func = <T extends Foo['union']>(one: T, two: Foo['object'][Capitalize<T>]) => { objectOne[one].func(two); }
遇到的错误
Property 'twoRelatedKey' is missing in type '{ oneRelatedKey: "oneRelatedValue"; }' but required in type '{ twoRelatedKey: "twoRelatedValue"; }'
期望效果
调用func时,参数类型严格关联,例如:
// 正确调用 func('one', { 'oneRelatedKey': 'foo' })
尝试过的方案
以下写法对单个字面量有效,但无法适配泛型参数的联合类型约束:
type FirstTestGeneric<T extends Foo['union']> = T; type FirstTest = FirstTestGeneric<'one'>; // 'one' type SecondTestGeneric<T extends Foo['union']> = Capitalize<T> extends keyof Foo['object'] ? Foo['object'][Capitalize<T>] : never; type SecondTest = SecondTestGeneric<'one'>; // {'oneRelatedKey': string} const testFunc = <T extends Foo['union']>(one: FirstTestGeneric<T>, two: SecondTestGeneric<T>) => { objectOne[one].func(two); }
问题根源在于泛型T接受的是整个联合类型,当传入联合类型时,Capitalize<T>会生成联合类型,导致Foo['object'][Capitalize<T>]变成所有对应类型的交集,这和函数参数的逆变特性有关。
解决方案
方案1:分布式条件类型绑定单个字面量
通过条件类型对每个字面量单独匹配,确保T对应的two参数类型唯一:
const func = <T extends Foo['union']>( one: T, two: T extends 'one' ? Foo['object']['One'] : T extends 'two' ? Foo['object']['Two'] : Foo['object']['Three'] ) => { // 由于objectOne的类型是推断而来,需手动断言类型兼容性 (objectOne[one].func as (obj: typeof two) => void)(two); };
方案2:映射类型生成参数联合类型(更简洁)
利用映射类型为每个Foo['union']成员生成对应的参数元组,再取元组的联合类型,让TS自动推断参数关联:
// 生成每个字面量对应的参数元组类型 type FuncParams = { [K in Foo['union']]: [one: K, two: Foo['object'][Capitalize<K>] ] }[Foo['union']]; // 将函数参数约束为元组联合类型 const func: (...args: FuncParams) => void = (one, two) => { objectOne[one].func(two); };
测试验证
- 正确调用:
func('one', { oneRelatedKey: 'foo' })通过类型检查 - 错误调用:
func('one', { twoRelatedKey: 'bar' })触发类型错误,符合预期
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

