我正在嘗試撰寫通用“groupBy”函式的打字稿簽名,該函式會將可區分型別聯合陣列“傳播”到記錄中,其中記錄的每個欄位都是可能的鑒別器值并指向具體物件的陣列從聯合中輸入。
例子:
interface Dog {
type: 'dog'
dogMetadata: {}
}
interface Cat {
type: 'cat'
catMetadata: {}
}
type Animal = Dog | Cat
const animals: Animal[] = [{ type: 'dog', dogMetadata: {} }, { type: 'cat', catMetadata: {} }]
每個介面都有一個共同的鑒別器屬性,沒有其他共同的屬性。
這是簡單的“groupBy”簽名,它不傳播型別聯合值,迫使我向下轉換記錄的值:
function groupBy<T, K extends string>(arr: T[], keyExtractor: (element: T) => K): Record<K, T[]>
const animalsByType: Record<'dog' | 'cat', Animal[]> = groupBy(animals, it => it.type)
const dogs: Dog[] = animalsByType['dog'] as Dog[] // Must downcast Animal[] to Dog[]
我怎樣才能創建一個知道區分聯合型別的具體型別的“groupBy”?我想要這樣的東西:
const animalsByType: { dog: Dog[], cat: Cat[] } = groupBy(animals, it => it.type)
const dogs: Dog[] = animalsByType['dog'] // animalsByType.dog is known to be Dog[] by typescript
實作很簡單,Typescript 部分有問題:) 我正在尋找一個不做假設的通用解決方案,比如鑒別器屬性的名稱或型別聯合中的型別數量。
后續問題
當聯合嵌套在另一個類中時,是否可以使相同的簽名起作用?
interface Holder<T> {
data: T
}
const animalHolders: Holder<Animal>[] = animals.map(data => ({ data }))
const dogHolders: Holder<Dog> = groupBy(animalHolders, it => it.data.type) // Any way of doing this?
游樂場鏈接
謝謝您的幫助。
uj5u.com熱心網友回復:
好問題...
讓我們首先創建一些實用程式型別:
type KeysOfType<O, T> = {
[K in keyof O]: O[K] extends T ? K : never;
}[keyof O];
這會將該點的所有鍵提取O到 type 的值T。這將用于將判別式的型別限制為string型別。它們將用作輸出型別中的鍵,因此我們對允許其他型別的判別式并不真正感興趣。
讓我們還添加Expand<T>以使我們的結果型別在智能感知中看起來更好。
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
現在,讓我們創建一個表示groupBy函式回傳型別的型別:
type Return<T, K extends KeysOfType<T, string>> =
{ [KK in string & T[K]]: { [_ in K]: KK } & T }
或者,可選地,自由地應用上述Expand<T>型別為消費者提供更好的智能感知:
type Return<T, K extends KeysOfType<T, string>> =
Expand<{ [KK in string & T[K]]: Expand<{ [_ in K]: KK } & T> }>
所以現在我們可以宣告函式:
function groupBy<T, K extends KeysOfType<T, string>>(
arr: T[],
keyExtractor: (element: T) => T[K]): Return<T, K>{
throw Error();
}
并稱之為:
const groups = groupBy(animals, e => e.type)
為了完全型別安全,無論選擇哪個鑒別器屬性。
游樂場鏈接
uj5u.com熱心網友回復:
有一個相當簡單的解決方案,它使用條件型別分布在聯合上的事實來擺脫不匹配的替代方案:
type GroupBy<T extends Record<D, PropertyKey>, D extends keyof T> =
{[K in T[D]]: T extends Record<D, K> ? T[] : never}
declare function groupBy<T extends Record<D, PropertyKey>, D extends keyof T>
(arr: T[], keyExtractor: (element: T) => T[D]): GroupBy<T, D>
它適用于原始示例,但也適用于其他區分鍵:
interface Orange { color: 'orange', juiceContent: number }
interface Banana { color: 'yellow', length: number}
type Fruit = Orange | Banana
const fruits: Fruit[] = [{color: 'orange', juiceContent: 250},{ color: 'yellow', length: 20}]
const fruitsByColor = groupBy(fruits, it => it.color)
const yellows = fruitsByColor.yellow
// const yellows: Banana[]
TypeScript 游樂場
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/449566.html
上一篇:從多個FormControl創建Observable
下一篇:迭代函式并連接結果
