簡體   English   中英

為什么我的 Typescript object 允許這個額外的屬性?

[英]Why is this extra property allowed on my Typescript object?

我們最近開始將 typescript 用於我們的 web 平台項目。

最大的優勢之一應該是強大的類型系統,它允許在編譯時檢查各種正確性(假設我們努力 model 並正確聲明我們的類型)。

目前,我似乎已經找到了類型系統能夠實現的限制,但它似乎不一致,我也可能只是使用了錯誤的語法。

我正在嘗試 model 我們的應用程序將從后端接收的對象類型,並使用類型系統讓編譯器在應用程序的任何地方檢查:

  1. 結構,即 TS 編譯器只允許在某個類型的對象上存在現有(枚舉)屬性
  2. 屬性類型檢查,即 TS 編譯器知道每個屬性的類型

這是我的方法的最小化版本(或直接鏈接到 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

然后這會失敗,因為它與該接口匹配 - 它沒有ModelSize屬性:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM