[英]How do I type an object with known and unknown keys in TypeScript
我正在尋找一種方法來為以下 object 創建 TypeScript 類型,它具有兩個已知密鑰和一個具有已知類型的未知密鑰:
interface ComboObject {
known: boolean
field: number
[U: string]: string
}
const comboObject: ComboObject = {
known: true
field: 123
unknownName: 'value'
}
該代碼不起作用,因為 TypeScript 要求所有屬性都匹配給定索引簽名的類型。 但是,我不想使用索引簽名,我想輸入一個我知道其類型但不知道其名稱的字段。
到目前為止,我唯一的解決方案是使用索引簽名並設置所有可能類型的聯合類型:
interface ComboObject {
[U: string]: boolean | number | string
}
但這有很多缺點,包括允許在已知字段上輸入不正確的類型以及允許任意數量的未知鍵。
有更好的方法嗎? TypeScript 2.8 條件類型有幫助嗎?
讓我們做一些類型操作來檢測給定的類型是否是聯合。 它的工作方式是使用條件類型的分配特性將聯合展開到組成部分,然后注意到每個組成部分都比聯合窄。 如果這不是真的,那是因為聯合只有一個組成部分(所以它不是聯合):
type IsAUnion<T, Y = true, N = false, U = T> = U extends any
? ([T] extends [U] ? N : Y)
: never;
然后使用它來檢測給定的string
類型是否是單個字符串文字(因此:不是string
,不是never
,也不是聯合):
type IsASingleStringLiteral<
T extends string,
Y = true,
N = false
> = string extends T ? N : [T] extends [never] ? N : IsAUnion<T, N, Y>;
現在我們可以開始處理您的特定問題。 定義BaseObject
作為部分ComboObject
,你可以直截了當地定義:
type BaseObject = { known: boolean, field: number };
並為錯誤消息做准備,讓我們定義一個ProperComboObject
以便當您搞砸時,錯誤會提示您應該做什么:
interface ProperComboObject extends BaseObject {
'!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!': string
}
主菜來了。 VerifyComboObject<C>
接受一個C
類型,如果它符合您想要的ComboObject
類型,則將它原封不動地返回; 否則,它會為錯誤返回ProperComboObject
(它也不符合)。
type VerifyComboObject<
C,
X extends string = Extract<Exclude<keyof C, keyof BaseObject>, string>
> = C extends BaseObject & Record<X, string>
? IsASingleStringLiteral<X, C, ProperComboObject>
: ProperComboObject;
它的工作原理是將C
分解為BaseObject
和其余的鍵X
。 如果C
與BaseObject & Record<X, string>
不匹配,那么您就失敗了,因為這意味着它要么不是BaseObject
,要么是具有額外非string
屬性的對象。 然后,它通過使用IsASingleStringLiteral<X>
檢查X
來IsASingleStringLiteral<X>
一個鍵。
現在我們創建一個輔助函數,它要求輸入參數匹配VerifyComboObject<C>
,並返回未更改的輸入。 如果您只想要一個正確類型的對象,它可以讓您及早發現錯誤。 或者你可以使用簽名來幫助你自己的函數需要正確的類型:
const asComboObject = <C>(x: C & VerifyComboObject<C>): C => x;
讓我們測試一下:
const okayComboObject = asComboObject({
known: true,
field: 123,
unknownName: 'value'
}); // okay
const wrongExtraKey = asComboObject({
known: true,
field: 123,
unknownName: 3
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
const missingExtraKey = asComboObject({
known: true,
field: 123
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
const tooManyExtraKeys = asComboObject({
known: true,
field: 123,
unknownName: 'value',
anAdditionalName: 'value'
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
第一個編譯,根據需要。 最后三個失敗的原因不同,與額外屬性的數量和類型有關。 錯誤消息有點神秘,但這是我能做的最好的。
您可以在Playground 中看到正在運行的代碼。
同樣,我認為我不建議將其用於生產代碼。 我喜歡玩弄類型系統,但感覺這個系統特別復雜和脆弱,我不想為任何不可預見的后果負責。
希望對你有幫助。 祝你好運!
不錯的一個@jcalz
它給了我一些很好的洞察力,可以到達我想要的地方。 我有一個具有一些已知屬性的 BaseObject,並且 BaseObject 可以擁有任意數量的 BaseObject。
type BaseObject = { known: boolean, field: number };
type CoolType<C, X extends string | number | symbol = Exclude<keyof C, keyof BaseObject>> = BaseObject & Record<X, BaseObject>;
const asComboObject = <C>(x: C & CoolType<C>): C => x;
const tooManyExtraKeys = asComboObject({
known: true,
field: 123,
unknownName: {
known: false,
field: 333
},
anAdditionalName: {
known: true,
field: 444
},
});
這樣我就可以對我已經擁有的結構進行類型檢查,而無需進行太多更改。
泰
我提出了一個類似的問題,我認為@matthew-dean 的建議要簡單得多,並且可以解決許多類似的問題。 它對我有用,所以我認為它應該被稱為可能的解決方案:
interface ComboObject {
known: boolean
field: number
[key: string]: string
}
const comboObject: ComboObject = {
known: true
field: 123
unknownName: 'value'
}
通過這種方法,您可以指定已知字段的類型並修復其余字段的類型。
使用此方法未完成的唯一要求是您不限制未知字段的數量。 為此,您將需要與其他答復中建議的方法不同的方法。
在我看來,更簡單的方法是使用聯合類型:
type ComboObject = {
known: boolean
field: number
} & {
[key: string]: string | number | boolean;
};
這告訴 TypeScript 從左邊繼承,但是當你向它提供額外的未知參數類型時不會生氣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.