Alexander Nechay Alexander Nechay - 2 months ago 7
TypeScript Question

Why do TypeScript's type guards work differently in the parent scope?

I have this code:

function isNumberArray(l: any[]): l is number[] {
let res = true;
for (let e of l) {
res = res && (typeof e === 'number');
}
return res;
}
let arr:(number|string)[] = [1, 2];
if (isNumberArray(arr)) {
let res1: number = arr[1]; //OK
let res2: number = [1].map(i => arr[i])[0]; // ERROR: Type 'string | number' is not assignable to type 'number'
}


For some reason, the variable arr has different types in the two lines inside if block. Why?

Answer

Great question. The root cause here is that TypeScript doesn't know that the callback is invoked immediately, and thus makes the conservative assumption that the function might be invoked later after other assignments may have occurred.

Consider some code like this:

let x: string | number = "hello world";
foo(() => console.log(x.substr(2)));
x = 42;

Does this code crash, or succeed? There's no way to know. It depends on whether foo executes its callback immediately, or later (e.g. foo is window.setTimeout). It could even do both!

Note that if you had said const instead of let, TypeScript can know that the variable didn't change types "later":

const arr:(number|string)[] = [1, 2];
if (isNumberArray(arr)) {
    let res1: number = arr[1]; //OK
    let res2: number = [1].map(i => arr[i])[0]; // OK
}
Comments