我有以下用例:我有一个Foo和Bar的联合。 Foo和Bar都有一个type属性,每个属性都有自己的枚举值" foo" &" bar"。此外,它们每个都有一个有效负载属性,Foo是一个字符串而Bar是一个数字。
enum Types {
FOO = "foo",
BAR = "bar"
}
interface Foo {
type: Types.FOO;
payload: string;
}
interface Bar {
type: Types.BAR;
payload: number;
}
type Union = Foo | Bar;
现在,我尝试使用交叉点缩小联合,我在此定义类型:
// Does not pass type check because payload must be a string
const baz: Union & { type: Types.FOO } = {
type: Types.FOO,
payload: 42
}
这不会传递类型检查(正如我所料),因为有效负载是一个数字,但当类型为FOO时应该是一个字符串。
另一方面,这编译(正如我所料):
// Passes type check
const baz: Union & {type: Types.FOO} = {
type: Types.FOO,
payload: "foobar"
}
现在我有一个函数handleFoo,我这样输入:
function handleFoo(foo: Union & {type: Types.FOO}) {
// payload is string | number
console.log(foo.payload)
if (foo.type === Types.FOO) {
// payload is still string | number??
console.log(foo.payload)
}
}
TS告诉我有效负载是一个字符串|号码联盟。但与此同时,这种类型无法构建,有效载荷是一个数字。另外,即使我检查类型是FOO,它仍然不会缩小foo的类型。
另一方面,如果我这样做,我会在FOO检查后得到预期的缩小。
function handleFoo2(foo: Union) {
console.log(foo.payload) // payload is string | number
if (foo.type === Types.FOO) {
// payload is string
console.log(foo.payload)
}
}
第二种方法(以及handleFoo中的常规类型检查)对我来说是不切实际的,因为该检查已经在handleFoo的调用者中执行。
这是一个错误,还是仅仅是对类型缩小的某种误解?
有没有办法解决这个问题,或者我应该向TypeScript团队提交错误?
答案 0 :(得分:0)
您从哪里得到的(我一直无法弄清楚)?
TS告诉我有效负载是一个字符串|数字联盟。但是同时,以有效载荷为数字无法构造该类型。
我将您的代码放在一起,当我检查可以分配给按类型过滤的各个函数的有效载荷的类型时,最终结果是我使用了谨慎的“字符串”或“数字”类型,无法获取“字符串|数字”。
因此,如果您想要的是“联合”类型的通用处理程序,只要它们与有效载荷的类型无关,Typescript就会在您的代码上警告您是否有不兼容的类型。请注意,我添加了一个没有检查的函数(有点像不带if检查的handleFoo2),并且只要声明了一致(即您不将对象Foo与数字有效载荷或带有字符串有效载荷的Bar对象。
我整理了一个可以在Typescript Playground上运行的示例,其中所有功能都起作用。为了方便起见,我还复制了以下源代码。 Typescript Playground Example
enum Types {
FOO = "foo",
BAR = "bar"
}
interface Foo {
type: Types.FOO;
payload: string;
}
interface Bar {
type: Types.BAR;
payload: number;
}
type Union = Foo | Bar;
const bar: Union & { type: Types.BAR } = {
type: Types.BAR,
payload: 5
};
const foo: Union & { type: Types.FOO } = {
type: Types.FOO,
payload: "foobar"
};
const fooUnion: Union = {
type: Types.FOO,
payload: "foobar"
}
const barUnion: Union = {
type: Types.BAR,
payload: 5
}
function handleFoo(foo: Union & {type: Types.FOO}) {
alert(typeof foo.payload);
// alert displays "string"
if (foo.type === Types.FOO) {
alert(foo.payload);
}
}
function handleFoo2(foo: Union) {
alert(typeof foo.payload);
// alert displays "string"
if (foo.type === Types.FOO) {
alert(foo.payload)
}
}
function handleUnion(union: Union) {
alert(typeof union.payload);
alert(union.payload);
}
function handleBar(foo: Union & {type: Types.BAR}) {
alert(typeof foo.payload);
// alert displays "number"
if (foo.type === Types.BAR) {
alert(foo.payload);
}
}
function handleBar2(foo: Union) {
alert(typeof foo.payload);
// alert displays "number"
if (foo.type === Types.FOO) {
alert(foo.payload)
}
}
handleFoo(foo);
handleFoo2(foo);
handleBar(bar);
handleBar2(bar);
handleFoo(fooUnion);
handleFoo2(fooUnion);
handleBar(barUnion);
handleBar2(barUnion);
handleUnion(fooUnion);
handleUnion(barUnion);
答案 1 :(得分:0)
尝试用Extract<Union, {type: Types.FOO}>
代替Union & { type: Types.FOO }