第一個示例工作的原因是因爲這兩種泛型都是由編譯器根據匿名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' –