typescript - 在后续的 Array.prototype.map 中类型缩小丢失
有人能帮忙解释以下类型检查错误吗:
interface Foo {
kind: 'foo';
a: string;
}
interface Bar {
kind: 'bar';
b: number;
}
type FooBar = Foo | Bar;
interface Container {
ids: number[];
fooBar: FooBar;
}
const cont: Container = {
ids: [1, 2],
fooBar: { kind: 'foo', a: 'a' },
};
switch (cont.fooBar.kind) {
case 'foo':
console.log(cont.fooBar.a); // OK
cont.ids.map((id) => {
console.log(`${id} - ${cont.fooBar.a}`);
// Property 'a' does not exist on type FooBar
// Property 'a' does not exist on type Bar
})
break;
case 'bar':
console.log(cont.fooBar.b); // OK
cont.ids.map((id) => {
console.log(`${id} - ${cont.fooBar.b}`);
// Property 'b' does not exist on type FooBar
// Property 'b' does not exist on type Foo
})
break;
}
操场上的现场示例。
答案#1
你可以通过将 fooBar 拿到一个本地的 const 来修复它,并在 switch 中使用 fooBar 而不是 cont.fooBar:
const { fooBar } = cont; // <====
switch (fooBar.kind) {
case 'foo':
console.log(fooBar.a); // OK
cont.ids.map((id) => {
console.log(`${id} - ${fooBar.a}`); // OK now
})
break;
case 'bar':
console.log(fooBar.b); // OK
cont.ids.map((id) => {
console.log(`${id} - ${fooBar.b}`); // OK now
})
break;
}
实时复制
最初我在每个case中都这样做了,但 TypeScript 对上述写法也能接受。
我认为这是因为 TypeScript 无法确定回调函数是同步的,并且由于如果回调是异步的,cont.fooBar 可能会被其他代码更改,因此它无法确定缩小后的类型仍然是正确的。将其抓取到本地可以消除这种疑虑。这个理论得到了这样的事实的支持:如果你把它写成 let { fooBar }=cont; 而不是 const { fooBar }=cont;,它又无法保持缩小后的类型。
