Reputation: 111
On this scenario:
type Type = {
a: string;
b: string;
} | {
a: number;
b: number;
};
const func = ({a, b}: Type): string => {
if (typeof a === 'string') {
return b;
}
return 'w/e';
}
func({a: 1, b: 'w/e'});
I get an error on return b;
stating
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
From what I understand by the error, Typescript interprets the Type type as
type Type = {
a: string | number;
b: string | number;
};
, but even if that is the case, why can't I call the func
function with a string and a number?
The error on the call states
Argument of type '{ a: number; b: string; }' is not assignable to parameter of type 'Type'.
Type '{ a: number; b: string; }' is not assignable to type '{ a: number; b: number; }'.
Types of property 'b' are incompatible.
Type 'string' is not assignable to type 'number'.
(ts version: 4.7.2)
The function call with the wrong types was just to test my hypothesis on why I get the previous error. Why do I get an error at return b;
is what I don't understand.
Upvotes: 3
Views: 2128
Reputation: 951
TS cannot track the relationship of a
and b
after you destruct them, so they both be inferred string | number
. TS 4.6 introduced a limited control flow analysis for destructing, which only applies to discriminated union.
Upvotes: 4
Reputation: 1505
In line with the earlier answers, you can make the discriminant
optional, and with a little tweak in your function, which set the value of the discriminant at runtime
, you can have the same output.
type Type = {
__?: 'string',
a: string;
b: string;
} | {
__?: 'number',
a: number;
b: number;
};
const func = (data: Type) => { // const func: (data: Type) => string
data.__ = typeof data.a as Type['__']
if (data.__ === 'string') {
return data.b;
}
return 'w/e';
}
const test1 = func({a: 'hello', b: 'w/e'}); // const test1: string
const test2 = func({a: 1, b: 2}); // const test2: string
const test3 = func({a: 1, b: 'w/e'}); // const test3: string | Argument of type '{ a: number; b: string; }' is not assignable to parameter of type 'Type'.
Upvotes: 1
Reputation: 111
Ok, the answer is Discriminated Unions
.
I have to use a discriminant, a common property in each element of the union.
That, discriminant, property:
isPrimary: true
, not isPrimary: boolean
.Then I can check for that property's value to assume the other properties' types.
Also, in Typescript versions < 4.6, If i have an object with a type specified as above, If i destructure it, then the new variables are considered independent and I will continue getting an error similar to the initial example. Destructuring works in version 4.6 or higher.
Helpful links:
Thank you @Austaras and @coglialoro
Upvotes: 4