TypeScript Version: 3.1.1
Search Terms:
Code
interface A {
hasValue: true;
value: string;
}
interface B {
hasValue: false;
}
function checkOne(args: A | B) {
return args.hasValue && args.value;
// ^^^^^ this is ok, 'args' infers type A
}
function checkTwo(args: A | B) {
const { hasValue } = args;
return hasValue && args.value;
// ^^^^^ Error: Property 'value' does not exist on type 'B'.
}
Expected behavior:
Both checkOne and checkTwo compiled without errors.
Actual behavior:
Function checkTwo has compilation errors.
Playground Link: typescriptlang.org
Related Issues: I can't find related issues, but I am sure this is already known issue.
It does not "lose" type inference, the type inference just does not work the way you expect it. You're not checking args.hasValue (which would narrow the type to A), you're checking hasValue. The TypeScript compiler does not make the connection "hasValue comes from args, so by checking hasValue I can narrow args". It's unrelated to the destructuring, you have the same issues if you just assign it to a variable: const hasValue = args.hasValue.
There are numerous issues about this exact issue, that the type narrowing is not tracked across variables.
Most helpful comment
It does not "lose" type inference, the type inference just does not work the way you expect it. You're not checking
args.hasValue(which would narrow the type toA), you're checkinghasValue. The TypeScript compiler does not make the connection "hasValuecomes fromargs, so by checkinghasValueI can narrowargs". It's unrelated to the destructuring, you have the same issues if you just assign it to a variable:const hasValue = args.hasValue.There are numerous issues about this exact issue, that the type narrowing is not tracked across variables.