Consider the following three types in which MainType
is union of Type1
and Type2
. If kind
is \"kind1\"
then
By using destructuring at level of function argument we loose connection between kind
and data
. So switch by kind
is not narrowing the data
as now they are in different data structures.
I can say you remove the bound between kind
and data
what means that you really introduce two variables, one with type kind1 | kind2
and second with type { msg: string; } | { msg2: string; }
.
In result we don't have discriminant in form of kind
anymore.
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.
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. 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.
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.