Dealing With Temporal Uncertainty

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Dealing With Temporal Uncertainty

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Motivation

TypeScript doesn't do deep code flows and therefore can give weird errors, e.g.:

declare function getSuffix(): string | null;
declare function forExample(): string | null;

let suffix: string | null = getSuffix();
if (suffix != null) {
let exampleOne: string = 'jane' + suffix.toUpperCase();
['jane', 'john'].forEach((name) => {
let exampleTwo: string = name + suffix.toUpperCase(); // Compiletime Error: suffix possibly null
});
}

Reason is that code might genuinely have temporal issues e.g.:

let example: string | null = forExample();
if (example != null) {
setTimeout(() => {
console.log(example.toUpperCase()); // Runtime Error
});
}
example = null;

Fix

Store values so they cannot change depending on code execution path e.g.:

declare function getSuffix(): string | null;
declare function forExample(): string | null;

let suffix: string | null = getSuffix();
if (suffix != null) {
const suffixLocal = suffix;
let exampleOne: string = 'jane' + suffixLocal.toUpperCase();
['jane', 'john'].forEach((name) => {
let exampleTwo: string = name + suffixLocal.toUpperCase(); // Ok
});
}

This is how you would solve runtime issues as well e.g.:

let example: string | null = forExample();
if (example != null) {
const exampleLocal = example;
setTimeout(() => {
console.log(exampleLocal.toUpperCase()); // Ok
});
}
example = null;
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

Sometimes you do a perfectly valid type check on a variable, but then when you try to use the variable type check complaints that it's not the type that you expect it to be, the most likely cause for that is some form of temporal uncertainty. And in this lesson we will look at why this occurs and how you can solve it. So let's go to start off, here's an example of a real world use case that I've simplified where someone was confused why TypeScript was giving a compile time error. First, here's a portion of the code that works perfectly fine without any compile time errors. We get a string value from some API that might be a string

00:35

or now therefore we make sure that it is not now and then we use it as a proper string, for example, calling the function to uppercase. However, when we try to do the same thing going through a simple four H loop over a JavaScript array, we get a compile time error. And if you look at the error message, you can see that TypeScript is complaining that suffix could possibly be still now. Now of course you and I both know that there is absolutely no way for this budget to be possibly now, but test script still thinks so. So let's look at another example that explains why this is the case here.

01:08

We have an example variable that can be string or now and then we ensure that it is not now and try to use it as a string within a set timeout, but before the set timeout will actually execute, we set it now. So in this particular case types strip complaining that this can be possibly null is perfectly valid. It is indeed actually going to be null in this example, but this being now is pure coincidence as far as types TypeScript is concerned. Whenever you use a callback, any of the texts that you might have done on variables outside of that callback are essentially discarded

01:41

because TypeScript doesn't know when that callback will execute. In the first case, the callback is executed synchronously and therefore suffix cannot be possibly null. However, in the second case, the callback is executed asynchronously and therefore indeed example can be no. Now the solution to the problem is pretty simple. Once you've done a type check on a particular variable store the reference to that particular variable in a new one and TypeScript will automatically infer it to have that restricted type. So for the first example, it'll automatically infer suffix local to have the restricted type of type string and

02:15

therefore you can use it as a string perfectly fine anywhere within this particular conditional block. And we can repeat the same process for the second reference as well. We create a new variable called example local store. The current value of example in there and type strip will infer that it can only be a string and we can use it as a string anywhere within the IF block, without any issues. One of the big choices that the TypeScript team has to constantly make is the choice between type safety versus developer convenience. In this particular case, the TypeScript team has gone for type safety, but this makes sense because

02:48

after all, callbacks are traditionally a sign of delayed execution. And after a delay there's no certainty about any of the particular values that you might have tested previously.