2016-04-12 95 views
2

我寫了一個函數來投影陣列成地圖:打字稿類型推斷失敗?

function toMap<T,TKey,TElement>(items: Array<T>, 
     keySelector: (item: T) => TKey, 
     elementSelector: (item: T) => TElement 
    ): Map<TKey,TElement> { 

    var map = new Map<TKey,TElement>(); 
    for (var item of items) { 
     map.set(keySelector(item), elementSelector(item)); 
    } 
    return map; 
} 

基本上,遍歷列表並調用用戶提供的投影功能,提取地圖鍵和值。

注意,類型TKEY的和TElement從的投影函數的返回值infered在傳遞

大多數的時候,在地圖僅僅是原始的元素:

var personBySocial = toMap(people, person => person.ssn, person => person); 

所以我想讓第二個lambda默認。很容易的:

function toMap<T,TKey,TElement>(items: Array<T>, 
     keySelector: (item: T) => TKey, 
     elementSelector: (item: T) => TElement = item => item 
    ): Map<TKey,TElement> { 

    var map = new Map<TKey,TElement>(); 
    for (var item of items) { 
     map.set(keySelector(item), elementSelector(item)); 
    } 
    return map; 
} 

然而,這將產生一個編譯錯誤:

Type '(item: T) => T' is not assignable to type '(item: T) => TElement'. 
    Type 'T' is not assignable to type 'TElement'. 

出於某種原因,如果我在分別通過item => item,打字稿可以推斷,TElement == T,但如果我將它傳遞在默認情況下,它不能。

我在這裏做錯了什麼,或者這是目前的Typescript限制?如果是後者,是否有人知道這是未來將要解決的已知問題?


編輯,響應萊恩的評論:

function toMap<T,TKey>(items: Array<T>, keySelector: (item: T) => TKey): Map<TKey,T>; 
function toMap<T,TKey, TElement>(items: Array<T>, keySelector: (item: T) => TKey, elementSelector: (item: T) => TElement): Map<TKey, TElement>; 
function toMap<T,TKey, TElement>(items: Array<T>, keySelector: (item: T) => TKey, elementSelector?: (item: T) => TElement): Map<TKey, TElement> { 
    var map = new Map<TKey, TElement>(); 
    if (elementSelector) 
     for (var item of items) 
      map.set(keySelector(item), elementSelector(item)); 
    else 
     for (var item of items) 
      map.set(keySelector(item), item); 
    return map; 
} 

我運行到同一類型的錯誤,其中的else子句中的map.set(map.set(keySelector(item), item))被觸發的錯誤第二個item表示「T不能分配給TElement類型的參數」。

回答

0

我想你可能會更好過寫這兩個特徵:

function toMap<T,TKey>(items: Array<T>, keySelector: (item: T) => TKey): Map<TKey,T>; 
function toMap<T,TKey,TElement>(items: Array<T>, keySelector: (item: T) => TKey, elemSelector: (item: T) => TElement): Map<TKey,TElement>; 
/* implementation signature here */ 

這將是可解與generic type defaults(你會寫<T, TKey, TElement = T>),但那些沒有被尚未合併。

有關代碼的合法投訴,從編譯器的角度看,就是有人可能會手動指定所有類型的參數,但不指定可選參數:

let x = toMap<string, number, boolean>(someStringArray, someNumberFunc); 
+0

該申訴是非常合情合理的。當我嘗試實現第一個示例(兩個簽名)時,我遇到了相同類型的錯誤。我在我的帖子中包含了我的代碼。 – Mud