簡體   English   中英

如何告訴 TypeScript object[foo] 的類型是所有可能的 object.prop 類型的子集

[英]How to tell TypeScript that the type of object[foo] is a subset of all the possible object.prop types

例如,假設我有這個界面

interface Measurement {
  date: Date
  width: number
  height: number
  depth: number
  comment: string
}

然后我有以下代碼

// these are the strings representing those values
const measurement: Measurement = {}

const fields = ['date', 'width', 'height', 'depth', 'comment']
fields.forEach(field => {
  // Here I do a bunch of processing and after all that processing I know
  // for a fact that `field` can only be one of width | height | depth,
  // and therefore `measurement[field]` will take a number, for sure.

  measurement[field] = 6 // ಥ﹏ಥ
  // !! Type 'number' is not assignable to type 'Date & number & string'
})

所以我想問題是,盡管measurement[field]類型為Date | number | string我如何告訴 TS Date | number | string Date | number | string ,我知道它將具有類型number ,並且當我執行measurement[field] = 6時它不應該抱怨。

也許我只是用錯誤的方法看待這個問題。 如果是這樣的話,我應該怎么做呢?

謝謝!

默認情況下,為fields推斷的類型將是string[] ,這對於編譯器來說不夠具體,無法進行您希望在此處看到的那種控制流分析。 改變這一點的一種簡單方法是使用const斷言來告訴編譯器推斷它可以推斷出的最窄類型:

const fields = ['date', 'width', 'height', 'depth', 'comment'] as const;
// const fields: readonly ["date", "width", "height", "depth", "comment"]

您現在可以看到,已知fields是特定字符串文字類型的只讀元組。

然后,在您的forEach()回調中, field將被稱為這些字符串文字類型的聯合。 如果您使用類型保護來消除"comment""date"的可能性,編譯器將進一步縮小field的類型,直到它知道您可以根據需要為measurement[field]分配一個number

fields.forEach(field => {
    //  field: "date" | "width" | "height" | "depth" | "comment"
    if (field === "comment") return;
    if (field === "date") return;
    // field: "width" | "height" | "depth"
    measurement[field] = 6; // okay
})

如果您為消除"comment""date"而進行的處理過於復雜,編譯器無法理解,那么它不會縮小,您將收到您提到的錯誤:

fields.forEach(field => {
    //  field: "date" | "width" | "height" | "depth" | "comment"
    if (field === "COMMENT".toLowerCase()) return;
    if (field.split("").reverse().join() === "etad") return;
    // field: "date" | "width" | "height" | "depth" | "comment"
    measurement[field] = 6; // error!
    // Type 'number' is not assignable to type 'never'.
})

在這種情況下,您需要使用類型斷言或等效的方法來告訴編譯器您正在執行的操作是安全的。 例如:

function assert(x: any): asserts x {
    if (!x) throw new Error("BAD ASSERTION");
}

fields.forEach(field => {
    //  field: "date" | "width" | "height" | "depth" | "comment"
    if (field === "COMMENT".toLowerCase()) return;
    if (field.split("").reverse().join() === "etad") return;
    // field: "date" | "width" | "height" | "depth" | "comment";
    assert(field !== "comment" && field !== "date");
    // field: "width" | "height" | "depth"
    measurement[field] = 6; // okay
})

在這里,我們使用名為assert()斷言 function來告訴編譯器發生了什么。 assert()的實現會引發錯誤,但實際上我本可以忽略它。 或者,可能更直接:

fields.forEach(field => {
    //  field: "date" | "width" | "height" | "depth" | "comment"
    if (field === "COMMENT".toLowerCase()) return;
    if (field.split("").reverse().join() === "etad") return;
    // field: "date" | "width" | "height" | "depth" | "comment";
    measurement[field as "width" | "height" | "depth"] = 6; // okay
})

我們直接使用類型斷言。

Playground 代碼鏈接

你所要做的就是輸入它。

interface Measurement {
  date: Date
  width: number
  height: number
  depth: number
  comment: string
}

const measurement: Measurement = {}

const fields = ['date', 'width', 'height', 'depth', 'comment'];

fields.forEach(field => {
  // processing
  measurement[field] = 6 as Date & number & string;
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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