2016-07-31 113 views
1

考慮下面的代碼:打字稿泛型:參數類型推斷

function ensure<TModel, TValue>(accessor: { (obj: TModel): TValue; }) { 
} 

interface Person { 
    firstName: string; 
    lastName: string; 
} 

ensure((p: Person) => p.firstName); // <-- this works 
ensure<Person>(p => p.firstName); // <-- this does not work 

爲什麼是最後一行語法錯誤?

提供的參數不匹配呼叫目標的任何簽名。

爲什麼推斷p是any而不是Person

這裏有一個link到TypeScript操場中的代碼。

+0

附註:您可以對函數類型執行此操作:'訪問者:(obj:TModel)=> TValue' –

回答

2

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

希望能夠完全回答你的問題。

0

因爲我面臨着同樣的問題,實際上確實需要一個解決方案,而不僅僅是一個交代,這裏是我最後只是:

function NewTag<Attributes>(): { <Prototype>(P: Prototype): Prototype & { attributes: Attributes, view(attributes: Attributes): any } } { 
    return NewTag_Internal; 
} 

function NewTag_Internal(p) { // simplified to make it fit 
    return <any>{ attributes: Object.create(null), view() { }, __proto__: p } 
} 

var elm = NewTag<{ id:string }>()({ 
    log(str: string) { 
     console.log(str); 
    } 
}) 

elm.log(elm.attributes.id) 

有每次一對括號中的成本想要使用函數,但在我的情況,因爲它只是聲明的東西(所以不是很普遍的代碼),並啓用全自動完成=>交易是值得的。