Friday 16 March 2018

A TypeScript-powered moment of clarity

Today was a case where I forgot about one of the major strengths of TypeScript: The type-safety that it provides you and the features available to allow for writing code with maximum type-safety.

Consider this simple utility function in TypeScript

1
2
3
export const strIsNullOrEmpty(str: string | null | undefined): boolean {
    return str == null || str == "";
}

The function does what it says on the tin: Checks if the given string is null (or undefined) or an empty string and returns true if that's the case or false otherwise.

There's no problems with using this function, until you turn on null checks or use this function in a TS code-base with null checks enabled.


And if I hover over the red squiggly, I see this:


But how is that possible? I know that by calling this function, there's no way that the variable str could be null or undefined.

This is indeed the case ... at runtime.

At compile-time TypeScript does not know that. So short of spamming exclamation points all over your code, how can we tell TypeScript that the string we passed in cannot be null or undefined if we just checked for it?

The key is to change the function from the above example into this:

1
2
3
export function strIsNullOrEmpty(str: string | null | undefined): str is null | undefined {
    return str == null || str == "";
}

And the red squiggle goes away.

This is feature of TypeScript known as type guards. It's a way to write functions that evaluate to true/false at runtime, but at compile time allows you to make some assertions about the type of the variable you passed in to the TypeScript compiler. In our case, passing in a variable that could be a string or null or undefined has to be either null or undefined if this function returns true. If it returns false, then it has to be a non-null string, which TypeScript knows then to strip of the null | undefined on any subsequent if block following that function call. This is what's known in TypeScript as type narrowing.

Type guards have been in TypeScript for a long time, but it took today for me to realise that some of my utility functions (like this example) should've been written as type guards, especially with the advent of TypeScript 2.0 and its powerful null checking capabilities.

Now to double check places in my code where I've been sprinkling ! operators all over the place.

No comments: