我有以下代碼:
const a = {
type: "a",
childrenA: [ 1, 2, 3 ]
} as const;
const b = {
type: "b",
childrenB: [ 4, 5, 6 ]
} as const;
const HIERARCHY = {
a: "childrenA",
b: "childrenB",
} as const;
const getChildren = <T extends typeof a | typeof b>(obj: T) => {
const ret = obj[HIERARCHY[obj.type]];
return ret;
};
const AOrB = Math.random() > 0.5 ? a : b;
getChildren(AOrB);
我有多個不同型別的物件,它們都使用不同的鍵來存盤一個公共陣列,在這種情況下a存盤一個數字陣列 behindchildrenA和bbehind childrenB。我正在嘗試撰寫一個getChildren()可以從我的物件中檢索公共陣列的函式。要知道陣列存盤在哪個鍵下,有一個將HIERARCHY物件型別映射到相關鍵的物件。
的代碼相對簡單,但我不知道如何在 typescript 嚴格模式下輸入。
我相信我理解這個問題。
obj.type是型別"a" | "b"HIERARCHY[obj.type]是型別"childrenA" | "childrenB"- 訪問
a["childrenA"]和b["childrenB"]作業,但我沒有正確地告訴 ts 我將始終訪問正確的屬性。
uj5u.com熱心網友回復:
正如您所指出的,問題在于編譯器只看到 和 的型別HIERARCHY[obj.type]都是obj聯合型別,但它沒有辦法表示它們相互關聯的事實。它只知道你想用 like 型別{childrenA: number[]} | {childrenB: number[]}的索引索引到 like 型別的物件"childrenA" | "childrenB"。一般來說,這樣做是無效的(例如,obj[HIERARCHY[Math.random()<0.5 ? "a" : "b"]])。我們知道 ifobj有一個"childrenA"屬性, then HIERARCHY[obj.type]will be "childrenA",但是編譯器不跟蹤運算式的身份,只跟蹤它們的型別。
這是我一直稱之為“相關聯合型別”的一般問題,是microsoft/TypeScript#30581的主題。很長一段時間以來,我必須給人們的唯一建議是使用型別斷言,例如
const getChildrenAssert = <T extends typeof a | typeof b>(
obj: T): readonly number[] => (
obj as unknown as Record<"childrenA" | "childrenB", readonly number[]>
)[HIERARCHY[obj.type]];
但是microsoft/47109建議使用分布式物件型別,這是您在一組鍵上創建映射型別時得到的,然后立即使用該組鍵對其進行索引。也就是說,一種形式的型別{[P in K]: F<P>}[K]},其中K是一些 keylike 型別(或此型別別的聯合),并且F<P>是對 keylike 型別進行操作的型別函式。這種形式最終分布F<P>在K. 也就是說,如果K是K1 | K2 | K3,則分配物件型別的計算結果為F<K1> | F<K2> | F<K3>。
對于您的情況,它可能如下所示:
type Mapper = typeof HIERARCHY;
type Obj<K extends keyof Mapper> = { [P in K]:
{ type: P } & Record<Mapper[P], readonly number[]>
}[K]
如果我們插入"a",Obj我們會得到你的型別a:
type A = Obj<"a">;
/* type A = {
type: "a";
} & Record<"childrenA", readonly number[]> */
和同樣的"b":
type B = Obj<"b">;
/* type B = {
type: "b";
} & Record<"childrenB", readonly number[]> */
如果我們插入 union "a" | "b",我們會得到 union typeof a | typeof b:
type AOrB = Obj<"a" | "b">
/* type AOrB = ({
type: "a";
} & Record<"childrenA", readonly number[]>) | ({
type: "b";
} & Record<"childrenB", readonly number[]>) */
現在我們在該鍵型別中進行getChildren() 泛型:
const getChildren = <K extends keyof Mapper>(
obj: Obj<K>): readonly number[] => obj[HIERARCHY[obj.type]]; // okay
而且完全沒有錯誤。編譯器將obj.type其視為 type K,并且該泛型型別始終保持泛型HIERARCHY[obj.type]和obj[HIERARCHY[obj.type]]。編譯器將生成的輸出視為可分配給readonly number[],并且一切都按預期作業。
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/497067.html
標籤:打字稿
