第一个示例工作的原因是因为这两种泛型都是由编译器根据匿名lambda函数中传入的类型推断的。
不幸的是,在打字稿消费类功能时,它的全有或全无 - 你必须提供两种:
- 类型匹配函数的签名的所有仿制药,或
- 没有仿制药,如果你希望编译器“猜测”最适合您的呼叫匹配函数签名,同时自动推断类型(如果这样的推断是在所有可能的)
注意,如果一个类型不能推断它是默认的假设是的类型: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
希望能够完全回答你的问题。
附注:您可以对函数类型执行此操作:'访问者:(obj:TModel)=> TValue' –