我想獲得這樣的 Narrowing 型別:
type Expected = {
method: 'firstNamespace/firstMethod'
payload: [string]
} | {
method: 'firstNamespace/secondMethod'
payload: [number, number]
} | {
method: 'secondNamespace/firstMethod'
payload: [number]
}
從這樣的方法物件:
const Methods = {
firstNamespace: {
firstMethod: (p: string) => {},
secondMethod: (p: number, k: number) => {}
},
secondNamespace: {
firstMethod: (p: number) => {}
}
}
我嘗試了各種方法,但我認為我在 TypeScript 中有一些誤解。
我已經使用 TypeScript 將近一年了,任何超出基本型別的東西似乎都超出了我的能力范圍......
uj5u.com熱心網友回復:
這在 TypeScript 中是可能的,通過遞回條件型別深入到 的嵌套屬性Methods,以及模板文字型別在型別級別將方法名稱連接在一起。
讓我們呼叫您正在尋找的型別函式 as MethodsToExpected<T>,它接受一個物件型別T并生成一個物件型別的聯合,其method屬性是"/"每個方法名稱的分隔路徑,其payload屬性是相應方法的引數型別的元組。然后我們可以MethodsToExpected<T>像這樣根據自身遞回定義:
type MethodsToExpected<T> = { [K in keyof T]-?:
T[K] extends (...args: infer P) => any ? { method: K, payload: P } :
MethodsToExpected<T[K]> extends infer X ? (
X extends { method: infer M, payload: infer P } ? (
{ method: `${Extract<K, string>}/${Extract<M, string>}`; payload: P }
) : never
) : never
}[keyof T]
構造{[K in keyof T]-?: XXX}[keyof T],它立即用它的鍵索引到一個映射型別,產生所有XXX型別的聯合。
對于K來自 的每個屬性鍵T,我們檢查屬性型別T[K]。如果它是一個函式型別,那么我們獲取它的引數串列串列并立即回傳{method: K, payload: P}。否則,我們會申請MethodsToExpected<T[K]>該物業并對其進行檢查。
(題外話:我使用的是有條件的型別推斷“復制”MethodsToExpected<T[K]>到一個新的型別引數X,然后我在為檢查型別使用分配條件型別,以便在任何工會X被分發到最終的工會如果你只是使用。MethodsToExpected<T[K]> extends {method: infer M, payload: infer P}而不是的中間X,它會產生類似的東西,{method: "a/b" | "a/c", payload: [string] | [number]}而不是所需的{method: "a/b", payload: [string]} | {method: "a/c", payload: [number]}。)
對于 的每個聯合元素MethodsToExpected<T[K]>,我們提取method型別M和payload型別P,并構建一個新的method/payload對。該payload型別不會改變,只是P,但我們前面加上當前密鑰K和一個斜線"/"到M與模板文本型別。編譯器不能確定K和M都是string型別,所以它不想讓你`${K}/${M}`直接寫。相反,我們使用的Extract<T, U>實用型說服編譯器,我們將只串聯string秒。
讓我們看看它是否有效:
type Expected = MethodsToExpected<typeof Methods>;
/* type Expected = {
method: "firstNamespace/firstMethod";
payload: [p: string];
} | {
method: "firstNamespace/secondMethod";
payload: [p: number, k: number];
} | {
method: "secondNamespace/firstMethod";
payload: [p: number];
} */
看起來挺好的。為了確保它深入到嵌套的子屬性,讓我們嘗試一個不同的:
type Nested = MethodsToExpected<{ a: { b: { c: { d: { e: (f: string) => number } } } } }>;
/* type Nested = {
method: "a/b/c/d/e";
payload: [f: string];
} */
也很好。
Playground 鏈接到代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/315774.html
標籤:javascript 打字稿 目的 类型 缩小
