简体   繁体   English

如何使 Discriminated Unions 与解构一起工作?

[英]How to make Discriminated Unions work with destructuring?

Consider the following three types in which MainType is union of Type1 and Type2 .考虑以下三种类型,其中MainTypeType1Type2并集。 If kind is "kind1" then data should be of type {msg: string} same from Type2如果kind"kind1"那么data的类型应该是{msg: string}Type2相同

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

type MainType = Type1 | Type2;

Here is the first way to use it.这是使用它的第一种方法。

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

The above code gives no error and shows correct autocomplete.上面的代码没有给出错误并显示正确的自动完成。

But when we destructure the obj then it gives error.但是当我们解构obj它会出错。

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

The error is错误是

Property 'msg' does not exist on type '{ msg: string; } | { msg2: string; }'属性'msg'不存在于类型'{ msg: string; } | { msg2: string; }' '{ msg: string; } | { msg2: string; }'

Maybe its something very basic.也许它的一些非常基本的东西。 But I am new to ts so I can't get how destructuring changes the types.但我是 ts 的新手,所以我无法了解解构如何改变类型。 Please explain the reason and also tell is there any way to fix it.请解释原因,并告诉有什么方法可以解决它。

By using destructuring at level of function argument we loose connection between kind and data .通过在函数参数级别使用解构,我们松散了kinddata之间的联系。 So switch by kind is not narrowing the data as now they are in different data structures.因此,按kind切换不会像现在那样缩小data因为它们处于不同的数据结构中。

I can say you remove the bound between kind and data what means that you really introduce two variables, one with type kind1 | kind2我可以说你删除了kinddata之间的界限,这意味着你真的引入了两个变量,一个类型为kind1 | kind2 kind1 | kind2 and second with type { msg: string; } | { msg2: string; } kind1 | kind2和第二个类型为{ msg: string; } | { msg2: string; } { msg: string; } | { msg2: string; } { msg: string; } | { msg2: string; } . { msg: string; } | { msg2: string; } .

In result we don't have discriminant in form of kind anymore.结果我们不再有kind形式的判别kind了。

Below equivalent code to destructuring behavior:下面是解构行为的等效代码:

const f = (t: MainType) => {
  const kind = t.kind // "kind1" | "kind2";
  const data = t.data // {msg: string;} | {msg2: string;}
}

And yes from the logic perspective your code is fully ok, it should work as we know the relation between these fields.是的,从逻辑角度来看,您的代码完全没问题,因为我们知道这些字段之间的关系,所以它应该可以工作。 Unfortunately TS is not able to understand the bound.不幸的是,TS 无法理解界限。

In summary - unfortunate until your don't narrow the type to specific member of the union, you cannot use destructuring, as it will ruin the type relationship between fields.总之- 不幸的是,除非您没有将类型缩小到联合的特定成员,否则您不能使用解构,因为它会破坏字段之间的类型关系。


We can think about workaround by some type guards.我们可以考虑一些类型保护的解决方法 Consider following example:考虑以下示例:

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 }
  }
}

By using type guards isKind1 and isKind2 we are able to create a connection between these two variables.通过使用类型保护isKind1isKind2我们能够在这两个变量之间创建连接。 But the issue is we cannot use switch anymore, we also have more code, and field relation implemented in functions and not type definitions, such approach is error prone as I can do different relation in function then the original type is defining.但问题是我们不能再使用switch ,我们还有更多的代码,以及在函数中实现的字段关系而不是类型定义,这种方法容易出错,因为我可以在函数中做不同的关系,然后原始类型正在定义。

To be clear I am showing it is possible but its not worth the candle and I suggest to keep the original implementation without destructuring.明确地说,我正在展示它是可能的,但它不值得,我建议保留原始实现而不破坏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM