TypeScript Version: 3.4.0-dev.201xxxxx
let a: number | undefined;
let b: number | undefined;
if (a || b) {
const c = a || b;
}
馃悰 c is wrongly assumed to be number | undefined when it should be number.
Probably a design limitation because it requires narrowing the type of an expression (instead of a variable, or property), but just wanted to point out the obvious; you can restructure your code to work around it.
let a: number | undefined;
let b: number | undefined;
const tmp = a || b;
if (tmp) {
const c = tmp;
}
Although, it would be cool to have narrowing for side-effect free expressions. Not sure if there's much benefit to it.
I never use logical operators with non-boolean types, though.
Yeah, the problem is that a || b, from the compiler's point of view, doesn't prove anything individually about a or b, so it can't actually narrow them. At the time the assignment to c happens the types are still as follows:
a :: number | undefined
b :: number | undefined
So as far as the compiler is concerned, both could be undefined at the time the assignment to c happens. We know that's not the case since a || b requires at least one of them to be non-falsy, but the type narrowing mechanism isn't quite that nuanced. It doesn't track dependencies between variables--the same reason this doesn't work:
let x: number | undefined
let isNumber = typeof x === 'number';
if (isNumber) {
// x :: number | undefined
x += 1; // error
}
I believe we just encountered this limitation and were scratching our heads:
interface TestInterface {
value?: string;
}
function foo({ value }: TestInterface) {
const isValidValue = !!value && /bar/.test(value);
const thing = isValidValue ? value.split('x') : [];
// ^ Object is possibly 'undefined'
}
This is a simplified example of what we were trying within our code. "Clearly" if value is falsy, there's no way it would be undefined when checking isValidValue.
Most helpful comment
Yeah, the problem is that
a || b, from the compiler's point of view, doesn't prove anything individually aboutaorb, so it can't actually narrow them. At the time the assignment tochappens the types are still as follows:So as far as the compiler is concerned, both could be
undefinedat the time the assignment tochappens. We know that's not the case sincea || brequires at least one of them to be non-falsy, but the type narrowing mechanism isn't quite that nuanced. It doesn't track dependencies between variables--the same reason this doesn't work: