As you are using let instead of const your type guard cannot guarantee that you will not modify the value, that said TS is taking the original assumption about the type (number | null), and you need to check it again. Changing y into const helps.
Examples of re-assigning y
Consider that you could be after the check, again set y into null.
function x(defValue: number | null) {
let y = defValue;
if (!y) {
return [];
}
// and below the change even though we checked that before the check is not safe
y = null;
return [1, 2, 3, 4].map((z) => {
// no error
return z + y;
})
}
Another example how I can change the value is - in map itself, consider:
return [1, 2, 3, 4].map(z => {
y = null // changed to null!!
return z;
}).map((z) => {
// error as it can be null
return z + y;
})
The main reason why its not safe check is that we cannot say when callback will be called, fact that we declare it in this scope, does not mean it will be executed in the same time, for example if it would be Promise.then, time of execution is unknown. Consider such code:
function x(defValue: number | null) {
let y = defValue;
if (!y) {
return [];
}
httpClient.get('http://something').then((z: number) => {
// error correctly because y is null
return z + y;
});
y = null; // this code is executed before then
}
Another example would be exposing possible change of y outside. Consider:
function x(defValue: number | null) {
let y = defValue;
if (!y) {
return null
}
return {
f: (z: number) => z + y, // error as y can be null because yToNull can change it
yToNull: () => { y = null } // modify the y to null
}
}
const fg = x(1);
if (fg) {
fg.yToNull();
fg.f(2); // y is null inside the scope of f
}
Use const to have persistent guard
As you can see it is very unsafe to treat let variables as narrowed by guards in the whole closure.
With const such re-assign is not possible, and TS is able to infer the narrow type of the value in scope of closure.
function x(defValue: number | null) {
const y = defValue;
if (!y) {
return [];
}
// any change of y type is not possible with const
return [1, 2, 3, 4].map((z) => {
// no error as y cannot be re-assigned
return z + y;
})
}
y === nulland the error is the samemap()will ever run its callback (since the signature formap()can't represent this), and it doesn't spend the time checking thatynever gets reassigned after the closure is created (this would be a fairly expensive operation), so it errs on the side of saying thatymight benullinside the closure. See microsoft/TypeScript#9998 for the general issue. Good luck!