如何告诉TypeScript对象[foo]的类型是所有可能的object.prop类型的子集

丹尼尔·雷纳(Daniel Reina)

例如,假设我有此界面

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 ,我知道它将具有类型,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,直到它知道,你可以指定一个更进一步numbermeasurement[field],根据需要:

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()来告诉编译器正在发生什么。assert()throw的实现会引发错误,但实际上我可以忽略它。或者,可能更直接:

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
})

我们直接使用类型断言。

操场上的代码链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章