[英]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
})
我們直接使用類型斷言。
你所要做的就是輸入它。
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.