The TypeScript compiler uses control flow based type analysis to infer narrower types of variables. As you note, when you take bar: string | number
and set it to +bar
, the value of bar
will definitely be a number
immediately afterward. For the remainder of the scope, unless you redefine bar
again, TypeScript will know that bar
is a number
.
Except when it doesn't. Returning the anonymous function function(){return bar;}
introduces a closure where the value of bar
will remain unexamined indefinitely until such time as that returned function is called. At this point, the TypeScript compiler gives up. It doesn't know if someone will change the value of bar
to a string
before that function is called, so it doesn't keep the narrowed type within the clousre.
"Wait", you might say, "by inspection I can see that nothing can happen to the value of bar
after I return that function, so it's always a number
forever." And you're probably right. Unfortunately the compiler is not as smart as you, and giving up when encountering a closure is just one of many heuristics used to keep the problem of control-flow analysis tractable.
There is an excellent issue in GitHub about the difficulty with getting control-flow anlaysis "right": Microsoft/TypeScript #9998: Trade-offs in Control Flow Analysis. There are many cases where the compiler can be fooled into either failing to narrow a type (incompleteness) or over-narrowing a type (unsoundness). That's just the way it is, at least until we reach the technological singularity.
Hope that helps; good luck!
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…