I don't understand why type guard doesn't work in the example below...
When we have these interfaces,
interface ParamA {
name: 'A';
aaa: boolean;
}
interface ParamB {
name: 'B';
bbb: number;
}
Good
function func(param: ParamA | ParamB) {
switch (param.name) {
case 'A':
const aaa = param.aaa;
console.log(aaa); // boolean
break;
case 'B':
const bbb = param.bbb;
console.log(bbb); // number
break;
default:
break;
}
}
Bad
function func(param: ParamA | ParamB) {
const name = param.name; // just rewrite here
switch (name) {
case 'A':
const aaa = param.aaa;
// ^^^
console.log(aaa);
break;
case 'B':
const bbb = param.bbb;
// ^^^
console.log(bbb);
break;
default:
break;
}
}
Compiler throws errors like Property 'aaa' does not exist on type 'ParamB'.
I do not think there should be a difference in behaviors whether I can put it in a variable.
The version of TypeScript using is 2.8.3.
Can anyone explain this?
TypeScript follows possible paths of execution that our programs can take to analyze the most specific possible type of a value at a given position. It looks at these special checks (called type guards) and assignments, and the process of refining types to more specific types than declared is called narrowing.
In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.
switch
statement serves as type guard in 'good' snippet and doesn't do that in 'bad' one.
I do not think there should be a difference in behaviors whether I can put it in a variable.
There's no difference in behaviour, there's a difference in how TypeScript is able to analyze this code. Since the code is statically analyzed, the ways how param
type can be narrowed are limited.
In order for param
to be narrowed down to ParamA
or ParamB
it should be mentioned in type guard. Since it wasn't but name
variable was, param
won't be narrowed down, its type will need to be asserted in this case:
...
switch (name) {
case 'A':
const aaa = (<ParamA>param).aaa;
...
Typescript 4.4 got smarter and your example work out of the box :-)
Assigning the property to a variable looses the connection to the object. This would be very hard to follow for the compiler as you could change the string and the object variable independently:
var param: ParamA | ParamB;
param = someParamA;
const name = param.name;
param = someParamB;
switch (name) { // boom
So if you know for sure that you have not changed the variable you can use the type assertion but i would not do that for maintenance reason.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With