打字稿歧视联合缩小不起作用
Typescript discriminated union narrowing not working
我有以下(简化的)代码,我在其中尝试使用可区分的联合类型。当我尝试访问 data
时,TS 认为它可能是 undefined
,我不确定为什么。
type Return =
| {
isReady: 'yes';
data: { a: number };
}
| {
isReady: 'no';
data: false;
};
const myFunction = (): Return => {
return {
isReady: 'yes',
data: { a: 111 }
};
}
const { isReady, data } = myFunction()
if (isReady === 'yes') {
const { a } = data; // ERROR: Property 'a' does not exist on type '{ a: number; } | undefined'.
}
您可以尝试检查 data
类型:
type Return =
{
isReady: 'yes';
data: { a: number };
}
| {
isReady: 'no';
data: false;
};
const myFunction = (): Return => {
return {
isReady: 'yes',
data: { a: 111 }
};
}
const { isReady, data } = myFunction()
if (typeof data !== 'boolean') {
const { a } = data;
}
通过解构 myFunction()
的已区分联合结果,您不幸地破坏了编译器在 isReady
和 data
属性之间可以看到的任何相关性:
const { isReady, data } = myFunction()
// const isReady: "yes" | "no"
// const data: false | { a: number; }
这里,编译器认为isReady
和data
都是union types,每个都有两个成员。那不是 错误的 ,但是编译器现在只看到 isReady
和 data
这对本质上有 四个 可能的成员:
const oops: (
| { isReady: "yes", data: false }
| { isReady: "no", data: false }
| { isReady: "yes", data: { a: number } }
| { isReady: "no", data: { a: number } }
) = { isReady, data }
因此检查 isReady
对编译器可以跟踪的 data
没有影响。
TypeScript 对我一直称之为相关联合类型 的支持的限制是功能请求microsoft/TypeScript#30581. TypeScript 4.4 has introduced support for aliased conditions (as implemented in microsoft/TypeScript#44730) 的主题,您可以在其中存储类似isReady
放入变量中,编译器稍后将使用它来区分您的联合:
const result = myFunction();
const isReady = result.isReady;
if (isReady === 'yes') {
const { a } = result.data; // okay in TypeScript 4.4
}
the pattern of destructuring a discriminant property and a payload property into two local variables and expecting a coupling between the two is not supported as the control flow analyzer doesn't "see" the connection. For example:
type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };
function foo({ kind, payload }: Data) {
if (kind === 'str') {
payload.length; // Error, payload not narrowed to string
}
}
We may be able to support that pattern later, but likely not in this PR.
所以目前还没有办法做到这一点。
目前最可行的方法就是根本不解构,而是按照它们本来的用途来使用可区分的联合,作为具有判别式的单个对象 属性:
const result = myFunction();
if (result.isReady === 'yes') {
const { a } = result.data; // okay
}
我会在 microsoft/TypeScript#30581 将其添加到不断增加的问题列表中。
我有以下(简化的)代码,我在其中尝试使用可区分的联合类型。当我尝试访问 data
时,TS 认为它可能是 undefined
,我不确定为什么。
type Return =
| {
isReady: 'yes';
data: { a: number };
}
| {
isReady: 'no';
data: false;
};
const myFunction = (): Return => {
return {
isReady: 'yes',
data: { a: 111 }
};
}
const { isReady, data } = myFunction()
if (isReady === 'yes') {
const { a } = data; // ERROR: Property 'a' does not exist on type '{ a: number; } | undefined'.
}
您可以尝试检查 data
类型:
type Return =
{
isReady: 'yes';
data: { a: number };
}
| {
isReady: 'no';
data: false;
};
const myFunction = (): Return => {
return {
isReady: 'yes',
data: { a: 111 }
};
}
const { isReady, data } = myFunction()
if (typeof data !== 'boolean') {
const { a } = data;
}
通过解构 myFunction()
的已区分联合结果,您不幸地破坏了编译器在 isReady
和 data
属性之间可以看到的任何相关性:
const { isReady, data } = myFunction()
// const isReady: "yes" | "no"
// const data: false | { a: number; }
这里,编译器认为isReady
和data
都是union types,每个都有两个成员。那不是 错误的 ,但是编译器现在只看到 isReady
和 data
这对本质上有 四个 可能的成员:
const oops: (
| { isReady: "yes", data: false }
| { isReady: "no", data: false }
| { isReady: "yes", data: { a: number } }
| { isReady: "no", data: { a: number } }
) = { isReady, data }
因此检查 isReady
对编译器可以跟踪的 data
没有影响。
TypeScript 对我一直称之为相关联合类型 的支持的限制是功能请求microsoft/TypeScript#30581. TypeScript 4.4 has introduced support for aliased conditions (as implemented in microsoft/TypeScript#44730) 的主题,您可以在其中存储类似isReady
放入变量中,编译器稍后将使用它来区分您的联合:
const result = myFunction();
const isReady = result.isReady;
if (isReady === 'yes') {
const { a } = result.data; // okay in TypeScript 4.4
}
the pattern of destructuring a discriminant property and a payload property into two local variables and expecting a coupling between the two is not supported as the control flow analyzer doesn't "see" the connection. For example:
type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number }; function foo({ kind, payload }: Data) { if (kind === 'str') { payload.length; // Error, payload not narrowed to string } }
We may be able to support that pattern later, but likely not in this PR.
所以目前还没有办法做到这一点。
目前最可行的方法就是根本不解构,而是按照它们本来的用途来使用可区分的联合,作为具有判别式的单个对象 属性:
const result = myFunction();
if (result.isReady === 'yes') {
const { a } = result.data; // okay
}
我会在 microsoft/TypeScript#30581 将其添加到不断增加的问题列表中。