Skip to content Skip to sidebar Skip to footer

How To Properly Handle Let-variables With Callbacks In Typescript?

I struggle a bit with one common JavaScript pattern during using TypeScript. It's about: declaring some 'let' variable without setting to it any initial value set this value to th

Solution 1:

This is a combination of multiple things biting you.


The main issue is that the compiler does not perform its control flow analysis by walking down inside function calls, finding the implementation of that function, and checking whether or not the state of any closed-over variables has changed. This would be the "right" behavior, but it would be prohibitively expensive in terms of time and memory for the compiler to essentially simulate the running of every program in order to follow such state changes. The question "when a function is invoked, what should the compiler assume its side effects are?" is a hard one to answer, and there's a discussion in GitHub about this: microsoft/TypeScript#9998.

The current compiler essentially assumes that function calls have no side effects. So if a is unset before wait() is called, it will be considered unset by the compiler afterward also. This is not true in your case, but there are many, many cases where this is the desired behavior. It's a tradeoff. So we need to come up with some workaround whereby the compiler treats a as "possibly set" after wait() is called, without relying on the compiler to do this for us.


Your second approach where you initialize a to something like null or undefined is promising, but unfortunately you've run into another issue: that if you assign a value to a union-typed variable, control flow analysis will narrow the variable's type. So after let a: { bool: boolean } | null = null;, the compiler sees a as of type null until and unless it gets reassigned. See micrsoft/TypeScript#8513 for more information. This is, again, often a good thing. After all, if we weren't trying to work around the other issue, you'd want the compiler to think a is null there.

The workaround here (obliquely mentioned in the above issue) is probably to use a type assertion to tell the compiler to treat null not as being of type null, but as being of the union type { bool: boolean } | null:

let a = nullas { bool: boolean } | null;

Now you can call wait(), after which the inferred type of a has not changed (due to #9998) and is still the union. Then the other code below functions as expected:

if (a === undefined || a === null) return;
alert(a.bool); // okay

So, yay? It's not perfect by any means but it's about the best I can suggest given the current incarnation of TypeScript. Anyway, hope that helps; good luck!

Playground link to code

Post a Comment for "How To Properly Handle Let-variables With Callbacks In Typescript?"