为何显式指定泛型首个类型参数会导致其余参数类型推断失效?
TypeScript泛型显式指定第一个参数后,后续参数类型推断失效的问题
问题现象
在使用TypeScript泛型时,显式传递第一个类型参数会导致后续参数的类型自动推断失效,示例如下:
const useOneArg = <T = any>(argT: T) => { return { argT } } const oneArg = useOneArg(1); oneArg.argT // 类型为number(自动推断正常) const useTwoArgs = <T = any, R = any>(argT: T, argR: R) => { return { argT, argR } } const twoArg = useTwoArgs(2, "two"); twoArg.argT // number,twoArg.argR // string(全推断正常) const twoArgAgain = useTwoArgs<number>(2, "two"); twoArgAgain.argT // number,但twoArgAgain.argR // any(显式指定第一个参数后,第二个参数类型丢失)
实际场景中的类似问题:
const twoTypesOneArg = <T = any, R = any>(argR: R) => { return { data: [] as T[], argR } } const objTest1 = twoTypesOneArg(2); objTest1.data // any[],objTest1.argR // number(全推断正常) const objTest2 = twoTypesOneArg<number>(2); objTest2.data // number[],但objTest2.argR // any(显式指定T后,R的类型推断失效)
这是预期行为还是Bug?
这是TypeScript的预期行为。当你显式传递任意一个泛型参数时,TypeScript会默认停止对剩余泛型参数的自动推断,转而使用它们的默认类型(这里默认是any)。设计上TypeScript最初要求泛型参数要么全部由编译器推断,要么全部显式指定,后续版本才支持部分参数的显式指定+推断。
解决方法
1. 使用TypeScript 4.7+的泛型推断占位符_
TS 4.7及以上版本支持用_作为泛型参数的推断占位符,明确告诉编译器该参数需要自动推断:
// 针对第一个示例 const twoArgAgain = useTwoArgs<number, _>(2, "two"); twoArgAgain.argR // 类型为string // 针对实际场景示例 const objTest2 = twoTypesOneArg<number, _>(2); objTest2.argR // 类型为number
2. 调整泛型参数顺序
将需要通过参数自动推断的泛型参数放在前面,需要显式指定的放在后面:
// 调整泛型参数顺序:R(需要推断)在前,T(需要显式指定)在后 const twoTypesOneArg = <R = any, T = any>(argR: R) => { return { data: [] as T[], argR } } // 使用时用`_`占位让编译器推断R,显式指定T const objTest2 = twoTypesOneArg<_, number>(2); objTest2.data // number[],objTest2.argR // number
3. 柯里化(嵌套函数)
将函数拆分为两层,第一层接收需要显式指定的泛型参数,第二层接收用于推断的参数:
const twoTypesOneArg = <T = any>() => <R = any>(argR: R) => { return { data: [] as T[], argR } } // 先指定T,再传入参数让编译器推断R const objTest2 = twoTypesOneArg<number>()(2); objTest2.data // number[],objTest2.argR // number
4. 旧版本TS兼容方案(条件类型推断)
如果你的TS版本低于4.7,可以用条件类型结合infer强制编译器推断参数类型:
const twoTypesOneArg = <T = any, R = any>(argR: R extends infer R ? R : never) => { return { data: [] as T[], argR } } const objTest2 = twoTypesOneArg<number>(2); objTest2.argR // number
内容的提问来源于stack exchange,提问作者Winston Guess
相关产品推荐
相关产品推荐

