TypeScript的类型缩小在联合类型上不起作用。

huangapple go评论67阅读模式
英文:

TypeScript type narrowing not working for union type

问题

Type narrowing 在这些代码片段中为什么不起作用?

const xyz: { num: number } | { str: string }
if ("num" in xyz) {
	xyz // { num: number; } | { str: string; }
}

我甚至尝试添加了一个类型判别字段:

const xyz: { type: "num", num: number } | { type: "str", str: string }
if (xyz.type === "num") {
	xyz // { type: "num", num: number } | { type: "str", str: string }
}

我使用的是 TypeScript 版本 5.0.2。

英文:

Why is type narrowing not working in these snippets?

const xyz: { num: number } | { str: string }
if ("num" in xyz) {
	xyz // { num: number; } | { str: string; }
}

I even tried adding a type discriminant:

const xyz: { type: "num", num: number } | { type: "str", str: string }
if (xyz.type === "num") {
	xyz // { type: "num", num: number } | { type: "str", str: string }
}

I'm using Typescript version 5.0.2

答案1

得分: 1

当TypeScript类型检查器抱怨先前的错误或警告时,似乎会影响正确的类型缩小。

在我的情况下,解决方案是修复(或消除)未初始化变量xyz(以及其他先前的类型错误),然后类型缩小再次正常工作。

简而言之,看似不相关的类型错误可能会使类型检查器处于不良状态;确保首先修复简单的错误,然后再解决任何奇怪的错误。

英文:

It seems when the Typescript type-checker complains about prior errors or warnings, then that can throw off proper type narrowing.

In my case, the solution was to fix (or silence) the use of the un-initialized variable xyz (and other prior type errors), and type narrowing worked properly again.

TL;DR Seemingly unrelated type errors can put the type-checker in a bad state; Ensure simple errors are fixed first before tackling any weird errors.

答案2

得分: -1

Type narrowing在你的示例中不起作用,因为{ num: number } | { str: string }{ type: "num", num: number } | { type: "str", str: string }的类型联合不是互斥的,这意味着这种类型的变量可以拥有属于两个联合成员的属性。

在第一个示例中,即使你检查了xyz中的"num",xyz仍然可以是{ str: string }类型。这是因为xyz可以具有属性str: string,它是第二个联合成员的成员。

类似地,在第二个示例中,检查xyz.type === "num"只能将类型缩小到{ type: "num", num: number } | { type: "str", str: string },而不能缩小到具体的类型。因此,xyz仍然可以具有属性str: string

要使类型缩小在这些示例中起作用,你需要使用一个可以将类型缩小到联合成员的特定成员的类型保护。例如,你可以定义一个自定义的类型保护函数,如下所示:

function isNum(obj: { num?: unknown }): obj is { num: number } {
  return typeof obj.num === "number";
}

然后,你可以在代码中使用这个类型保护来缩小xyz的类型:

const xyz: { num: number } | { str: string } = { num: 42 };
if (isNum(xyz)) {
  xyz // { num: number }
}

类似地,对于第二个示例,你可以定义一个类型保护函数,如下所示:

function isNum(obj: { type?: unknown }): obj is { type: "num", num: number } {
  return obj.type === "num";
}

然后,你可以在代码中使用这个类型保护来缩小xyz的类型:

const xyz: { type: "num", num: number } | { type: "str", str: string } = { type: "num", num: 42 };
if (isNum(xyz)) {
  xyz // { type: "num", num: number }
}
英文:

Type narrowing is not working in your examples because the type union of { num: number } | { str: string } and { type: "num", num: number } | { type: "str", str: string } are not mutually exclusive, meaning that a variable of this type can have a property that belongs to both union members.

In the first example, even though you checked that "num" in xyz, xyz can still be of type { str: string }. This is because xyz can have the property str: string which is a member of the second union member.

Similarly, in the second example, checking xyz.type === "num" only narrows down the type to { type: "num", num: number } | { type: "str", str: string }, but not to a specific type. Therefore, xyz can still have the property str: string.

To make type narrowing work in these examples, you need to use a type guard that can narrow down the type to a specific member of the union. For example, you can define a custom type guard function like this:

function isNum(obj: { num?: unknown }): obj is { num: number } {
  return typeof obj.num === "number";
}

Then you can use this type guard in your code to narrow down the type of xyz:

const xyz: { num: number } | { str: string } = { num: 42 };
if (isNum(xyz)) {
  xyz // { num: number }
}

Similarly, for the second example, you can define a type guard function like this:

function isNum(obj: { type?: unknown }): obj is { type: "num", num: number } {
  return obj.type === "num";
}

Then you can use this type guard in your code to narrow down the type of xyz:

const xyz: { type: "num", num: number } | { type: "str", str: string } = { type: "num", num: 42 };
if (isNum(xyz)) {
  xyz // { type: "num", num: number }
}

huangapple
  • 本文由 发表于 2023年5月14日 21:56:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76247844.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定