Issue
Can anyone please explain to me why typescript is throwing an error here:
let a: number | undefined;
let b = [2,3,4,5,6];
for (let c of b) {
a = 3;
if (!a) {
continue;
}
b.filter(x => +x !== +a);
}
Solution
This is a general limitation of TypeScript. See microsoft/TypeScript#9998 for a full discussion.
When you take the variable a
of the union type number | undefined
and assign a number
to it (a = 3
), or do a truthiness check on it (if (!a) continue
), the type checker narrows its apparent type to just number
, after which you can treat it like a number
:
a = 3;
if (!a) { continue; }
a.toFixed(2); // okay
Unfortunately, the effects of narrowing do not cross function boundaries. Inside the callback x => +x !== +a
that you pass to b.filter()
, the type of a
is stubbornly number | undefined
instead of just number
, and the compiler considers +a
on a possibly-undefined
a
to be a programmer mistake.
The reason the compiler does not know that a
is a number
inside the callback is because it doesn't know when the callback will be run, or even if it will be run. Because of the way closures work, it is possible, at least syntactically, for a
to be undefined
when the callback runs:
for (let c of b) {
a = 3;
if (!a) { continue; }
a.toFixed(2); // okay
someRandomFunction(x => +x !== +a);
}
a = undefined; // maybe
You know a
is definitely a number
when you call someRandomFunction
, but for all you know, someRandomFunction
waits for an hour and then calls the callback, by which time a
has been set back to undefined
by some later statement. The type system has no way to mark that a function will call its callback parameter immediately, so the filter()
array method might as well be someRandomFunction
. Oh well.
Maybe it would be possible for the compiler to detect that for the entire lifetime of a
it never goes back to undefined
, but that would be hard to make performant. Or maybe it would be possible to introduce an immediate
annotation to the language so that filter()
could be seen as "safe"... and in fact there is an open feature request for this at microsoft/TypeScript#11498, but for now it's not implemented.
For now, there are only workarounds. By far the easiest one is to take the narrowed expression and assign it to a new const
, and then use the const
inside the callback:
const _a = a; // number
b.filter(x => +x !== +_a); // okay
Since a
is narrowed to number
at that point, when you write const _a = a
, the compiler infers _a
to be of type number
, not number | undefined
. There is no need to narrow _a
; it is always and forever number
in any scope in which it exists. So the callback x => +x !== +a
compiles without error.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.