TypeScript中关联参数与返回值的泛型类型收窄实现方案
TypeScript实现多参数与返回值的类型关联收窄
问题场景
当前代码中,我们希望函数的参数a、b和返回值保持类型关联:当a的类型被收窄时,b和返回值的类型也同步收窄。但原代码使用泛型T extends NumberType | StringType时,TypeScript无法在switch块中自动关联b的类型,且返回值无法直接赋值给T,出现类型错误。
原示例代码:
type NumberType = { readonly type: "number" readonly value: number } type StringType = { readonly type: "string" readonly value: string } function test<T extends NumberType | StringType>(a: T, b: T): T { switch (a.type) { case "number": { console.log("Number case: ", a, b) const result: NumberType = { type: "number", value: a.value + b.value // 报错:b.value可能是string } return result // 报错:无法将NumberType赋值给T } case "string": { console.log("String case: ", a, b) const result: StringType = { type: "string", value: a.value + b.value // 报错:b.value可能是number } return result // 报错:无法将StringType赋值给T } } }
解决方案
方法一:使用函数重载
函数重载可以明确指定不同输入类型对应的输出类型,让TypeScript精准识别参数与返回值的类型关联:
type NumberType = { readonly type: "number" readonly value: number } type StringType = { readonly type: "string" readonly value: string } // 重载签名:定义两种输入输出的类型关联 function test(a: NumberType, b: NumberType): NumberType; function test(a: StringType, b: StringType): StringType; // 实现签名:处理联合类型 function test(a: NumberType | StringType, b: NumberType | StringType) { switch (a.type) { case "number": { console.log("Number case: ", a, b) // 此时b会被自动收窄为NumberType return { type: "number", value: a.value + b.value } } case "string": { console.log("String case: ", a, b) // 此时b会被自动收窄为StringType return { type: "string", value: a.value + b.value } } } }
方法二:泛型结合类型断言(简洁版)
如果希望保留泛型写法,可以通过类型断言告诉TypeScript当前分支的b和返回值类型与a一致:
type NumberType = { readonly type: "number" readonly value: number } type StringType = { readonly type: "string" readonly value: string } type ValueType = NumberType | StringType; function test<T extends ValueType>(a: T, b: T): T { switch (a.type) { case "number": { console.log("Number case: ", a, b) const numB = b as NumberType; const result = { type: "number", value: a.value + numB.value } as T; return result; } case "string": { console.log("String case: ", a, b) const strB = b as StringType; const result = { type: "string", value: a.value + strB.value } as T; return result; } } }
方法三:分布式条件类型优化泛型
通过定义分布式条件类型,让TypeScript自动推导类型关联:
type NumberType = { readonly type: "number" readonly value: number } type StringType = { readonly type: "string" readonly value: string } type ValueType = NumberType | StringType; // 定义根据type字段获取对应类型的条件类型 type TypeByTag<T extends ValueType["type"]> = T extends "number" ? NumberType : StringType; function test<T extends ValueType["type"]>( a: TypeByTag<T>, b: TypeByTag<T> ): TypeByTag<T> { switch (a.type) { case "number": { console.log("Number case: ", a, b) return { type: "number", value: a.value + b.value } as TypeByTag<T>; } case "string": { console.log("String case: ", a, b) return { type: "string", value: a.value + b.value } as TypeByTag<T>; } } }
说明
- 函数重载是最直观的方案,TypeScript会根据传入的参数类型自动匹配对应的重载签名,完美实现类型同步收窄。
- 泛型方案适合需要复用类型逻辑的场景,通过类型断言或条件类型辅助TypeScript完成类型推导。
内容的提问来源于stack exchange,提问作者Lenny
相关产品推荐
相关产品推荐

