![](/img/trans.png)
[英]TypeScript: in recursive type, extra props allowed to be assigned in object declaration only
[英]Why is this extra property allowed on my Typescript object?
我們最近開始將 typescript 用於我們的 web 平台項目。
最大的優勢之一應該是強大的類型系統,它允許在編譯時檢查各種正確性(假設我們努力 model 並正確聲明我們的類型)。
目前,我似乎已經找到了類型系統能夠實現的限制,但它似乎不一致,我也可能只是使用了錯誤的語法。
我正在嘗試 model 我們的應用程序將從后端接收的對象類型,並使用類型系統讓編譯器在應用程序的任何地方檢查:
這是我的方法的最小化版本(或直接鏈接到 TS playground )
interface DataObject<T extends string> {
fields: {
[key in T]: any // Restrict property keys to finite set of strings
}
}
// Enumerate type's DB field names, shall be used as constants everywhere
// Advantage: Bad DB names because of legacy project can thus be hidden in our app :))
namespace Vehicle {
export enum Fields {
Model = "S_MODEL",
Size = "SIZE2"
}
}
// CORRECT ERROR: Property "SIZE2" is missing
interface Vehicle extends DataObject<Vehicle.Fields> {
fields: {
[Vehicle.Fields.Model]: string,
}
}
// CORRECT ERROR: Property "extra" is not assignable
interface Vehicle2 extends DataObject<Vehicle.Fields> {
fields: {
extra: string
}
}
// NO ERROR: Property extra is now accepted!
interface Vehicle3 extends DataObject<Vehicle.Fields> {
fields: {
[Vehicle.Fields.Model]: string,
[Vehicle.Fields.Size]: number,
extra: string // Should be disallowed!
}
}
當編譯器似乎完全能夠在第二種情況下禁止無效的屬性名稱時,為什么第三個接口聲明沒有引發錯誤?
如果你想象那些fields
是這樣的接口:
interface Fields {
Model: string;
Size: number;
}
(它是匿名完成的,但它確實匹配此接口,因為你的[key in Vehicle.Fields]: any
)
然后這會失敗,因為它與該接口不匹配 - 它沒有Model
或Size
屬性:
fields: {
extra: string
}
但是,這通過了:
fields: {
Model: string;
Size: number;
extra: string
}
因為匿名接口有Fields
接口的擴展 。 它看起來像這樣:
interface ExtendedFields extends Fields {
extra: string;
}
這些都是通過TypeScript編譯器匿名完成的,但您可以向接口添加屬性並使其與接口匹配,就像擴展類仍然是基類的實例一樣
這是預期的行為。 基本接口僅指定field
的最低要求,在typescript中沒有要求實現類字段和接口字段之間的完全匹配。 你在Vehicle2
上得到錯誤的原因不是extra
的存在,而是缺少其他字段。 ( Property 'S_MODEL' is missing in type '{ extra: string; }'.
底部錯誤“ Property 'S_MODEL' is missing in type '{ extra: string; }'.
如果使用條件類型存在這些額外屬性,您可以使用某種類型技巧來獲取錯誤:
interface DataObject<T extends string, TImplementation extends { fields: any }> {
fields: Exclude<keyof TImplementation["fields"], T> extends never ? {
[key in T]: any // Restrict property keys to finite set of strings
}: "Extra fields detected in fields implementation:" & Exclude<keyof TImplementation["fields"], T>
}
// Enumerate type's DB field names, shall be used as constants everywhere
// Advantage: Bad DB names because of legacy project can thus be hidden in our app :))
namespace Vehicle {
export enum Fields {
Model = "S_MODEL",
Size = "SIZE2"
}
}
// Type '{ extra: string; [Vehicle.Fields.Model]: string; [Vehicle.Fields.Size]: number; }' is not assignable to type '"Extra fields detected in fields implementation:" & "extra"'.
interface Vehicle3 extends DataObject<Vehicle.Fields, Vehicle3> {
fields: {
[Vehicle.Fields.Model]: string,
[Vehicle.Fields.Size]: number,
extra: string //
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.