Javascript 如何使受歧视的联合与分解一起工作?

Javascript 如何使受歧视的联合与分解一起工作?,javascript,typescript,Javascript,Typescript,考虑以下三种类型,MainType是Type1和Type2的联合。如果kind是的“kind1”则数据的类型应为{msg:string}与Type2 interface Type1 { kind: "kind1"; data: { msg: string }; } interface Type2 { kind: "kind2"; data: { msg2: string }; } type MainType = Type1 | Type2; 这里是使用它的第一

考虑以下三种类型,
MainType
Type1
Type2
的联合。如果
kind
的“kind1”
数据
的类型应为
{msg:string}
Type2

interface Type1 {
    kind: "kind1";
    data: { msg: string };
}
interface Type2 {
    kind: "kind2";
    data: { msg2: string };
}

type MainType = Type1 | Type2;
这里是使用它的第一种方法

function func(obj: MainType) {
    switch (obj.kind) {
        case "kind1": return obj.data.msg;
        case "kind2": return obj.data.msg2;
    }
}
上面的代码没有错误,显示正确的自动完成

但是,当我们分解
obj
时,它会给出错误

function func({kind, data}: MainType) {
    switch (kind) {
        case "kind1": return data.msg;
        case "kind2": return data.msg2;
    }
}
错误是

属性
'msg'
不存在于类型
'{msg:string;}{msg2:string;}'


也许这是很基本的东西。但是我是ts新手,所以我不知道分解是如何改变类型的。请解释原因,并说明是否有办法解决。

通过在函数参数级别使用解构,我们可以在
种类
数据
之间断开连接。因此,按
kind
切换并没有缩小
数据的范围,因为它们现在处于不同的数据结构中

我可以说你去掉了
kind
data
之间的界限,这意味着你真正引入了两个变量,一个是类型
kind1 | kind2
,另一个是类型
{msg:string;}{msg2:string;}

结果,我们不再有
种类
形式的判别式

以下是分解行为的等效代码:

const f = (t: MainType) => {
  const kind = t.kind // "kind1" | "kind2";
  const data = t.data // {msg: string;} | {msg2: string;}
}
是的,从逻辑的角度来看,你的代码是完全正确的,它应该工作,因为我们知道这些字段之间的关系。不幸的是,TS无法理解边界

总而言之-不幸的是,除非您没有将类型缩小到联合的特定成员,否则您不能使用解构,因为它会破坏字段之间的类型关系


我们可以考虑一些类型的卫兵来解决问题。考虑下面的例子:

const isKind1 = (kind: MainType['kind'], data: MainType['data']): data is Type1['data'] 
=> kind === 'kind1'
const isKind2 = (kind: MainType['kind'], data: MainType['data']): data is Type2['data'] 
=> kind === 'kind2'

const f = ({kind, data}: MainType) => {
  if (isKind1(kind, data)) {
    data // is { msg: string }
  }
  if (isKind2(kind, data)) {
    data // is { msg2: string }
  }
}
通过使用类型guards
isKind1
isKind2
我们能够在这两个变量之间创建一个连接。但问题是我们不能再使用
开关
,我们还有更多的代码,字段关系是在函数中实现的,而不是在类型定义中实现的,这种方法很容易出错,因为我可以在函数中创建不同的关系,然后定义原始类型

明确地说,我正在展示这是可能的,但它不值得吹嘘,我建议在不破坏结构的情况下保留原始实现