(另一个编辑:部分类型参数推断已被废弃/延迟,并且从未进入 TS3.1 或自 TS3.7 起(包括 TS3.7)的任何版本。哦,好吧)
编辑:TypeScript 3.1 即将推出的功能将允许部分类型参数推断(使您引用的示例有效),请参阅the pull request 了解更多详细信息。
原答案(适用于TypeScript
第一个示例有效的原因是编译器从传入的匿名 lambda 函数的类型推断出两个泛型。
不幸的是,在 TypeScript 中使用泛型函数时,要么全有,要么全无——你必须提供:
- 匹配函数签名的所有泛型类型,或
- 没有泛型,如果您希望编译器“猜测”与您的调用最匹配的函数签名,同时自动推断类型(如果这种推断可能的话)
请注意,如果无法推断类型,则默认假定其类型为:Object,例如:
function example<T>(a: any): T {
return a as T;
}
let test = example(123);
above example 中的变量test 的类型为{}。
同时指定泛型类型或在方法中指定参数的类型都是处理此问题的正确方法:
ensure<Person, string>(p => p.firstName);
ensure((p: string) => p.firstName);
您引用的错误是正确的,因为:函数ensure 不存在只接受一个泛型的签名。
这样做的原因是您可以拥有具有替代签名的函数,这些函数采用不同数量的泛型类型参数:
interface Example {
ensure<TModel, TValue>(accessor: { (obj: TModel): TValue; }): TValue;
ensure<TModel>(accessor: { (obj: TModel): any; }): any;
}
interface Person {
firstName: string;
lastName: string;
}
let test: Example;
// the method 'ensure' has now 2 overloads:
// one that takes in two generics:
test.ensure<Person, string>((p: Person) => p.firstName);
// one that takes only one generic:
test.ensure<Person>(p => p.firstName);
// when not specified, TypeScript tries to infer which one to use,
// and ends up using the first one:
test.ensure((p: Person) => p.firstName);
Playground 以上。
如果 TypeScript 不强制签名匹配,它就不会知道应该选择哪个签名。
现在回答你问题的另一部分:为什么在没有明确说明泛型的情况下调用函数时,p 被假定为 any:
一个原因是编译器不能对其可能的类型做出任何假设,TModel 是不受约束的并且可以是任何东西,因此p 的类型是any。
您可以将泛型方法约束到接口,如下所示:
ensure<TModel extends Person, TValue>(accessor: { (obj: TModel): TValue; });
现在,如果您在没有指定参数类型或泛型类型的情况下调用该函数,它将被正确推断为Person:
ensure(p => p.firstName); // p is now Person
希望这能完全回答您的问题。