TypeScript 多个泛型参数为何不能只指定第一个并自动推断其余参数?
数据阿明
TypeScript 不支持在这种调用中显式指定 T,同时从实参推断未填写的 U。原签名中的两个类型参数都没有默认值,因此 b<User>(...) 会报类型参数数量不足。
分两次调用时,T 和 P 属于不同的函数。外层显式指定 T,内层不填写类型参数,编译器就能从实参推断 P。
extends 为什么没有让 U 自动变成可省略参数?
原签名是:
function b<T, U extends Partial<T>>(u: U) {
return u;
}
这段代码中:
U是独立的类型参数。U extends Partial<T>要求U可以赋值给Partial<T>。- 类型参数能否省略,取决于调用时的推断规则以及是否声明了默认类型。
extends 声明的是约束,不会为 U 设置默认类型,也不会开启部分类型参数推断。本次调用显式填写了类型参数列表,而 T、U 都是必填项,因此需要同时提供两个类型参数。
假设 User 定义如下:
type User = {
name: string;
age: number;
};
同时指定两个类型参数可以满足原签名:
const result = b<User, { name: string }>({ name: 'string' });
// result 的类型为 { name: string }
不过,这样需要重复描述实参类型。
为什么分两次调用可以保留类型推断?
将两个类型参数放在不同的函数签名中,就可以分别显式指定和自动推断:
function b<T>() {
return function<P extends Partial<T>>(p: P) {
return p;
};
}
const result = b<User>()({ name: 'string' });
// result 的类型为 { name: string }
代码分两步进行类型检查:
b<User>()确定外层的T,并返回带有P extends Partial<User>约束的泛型函数。- 内层函数从实参
{ name: 'string' }推断P,再检查它是否满足约束。
这里通常推断出的是 { name: string }。使用泛型本身不会自动保留字符串字面量类型 'string'。
如果要多次使用同一种 T,可以保存外层调用的结果:
const userPartial = b<User>();
const named = userPartial({ name: 'Alice' });
// { name: string }
const aged = userPartial({ age: 30 });
// { age: number }
userPartial({ age: '30' });
// 类型错误:string 不能赋值给 number
给 U 设置默认值能否达到同样效果?
设置默认类型可以消除类型参数数量错误:
function b<T, U extends Partial<T> = Partial<T>>(u: U) {
return u;
}
const result = b<User>({ name: 'string' });
// result 的类型为 Partial<User>
但在这个调用中,显式指定 T 并省略 U 后,U 会使用默认类型 Partial<User>,不会从对象实参推断为 { name: string }。
如果可以接受 Partial<User> 返回类型,可以使用默认值;如果希望返回类型保留实参实际提供的属性,应使用前面的分层调用写法。
只需要检查参数时,可以简化签名
如果不需要保留实参的具体类型,直接使用 Partial<T> 即可:
function b<T>(u: Partial<T>): Partial<T> {
return u;
}
const result = b<User>({ name: 'string' });
// result 的类型为 Partial<User>
Partial<T> 只把属性变为可选,不会改变属性原有的值类型,也不会进行运行时校验。extends 约束同样不保证对象只能包含 T 中的属性,不应把它当作严格的字段白名单。
备注:内容仅供参考。
