typescript: using the nonnullable utility to avoid bugs

2021-09-18

 | 

~2 min read

 | 

335 words

When writing an interface for a function, it’s common to want to allow a range of types. This can often be communicated with a generic.

For example, imagine a function toString:

partial.ts
function toString<T>(x: T): string {
  return x.toString()
}

Our function will take a type T and returns a string. In Javascript, the Object prototype includes a toString method and (almost) everything in Javascript is an object. Typescript is now smart enough to know that not every T has a toString method on its prototype, so the compiler will complain about this function now, but it wouldn’t about a variant - for example:

partialAlt.ts
function toString<T>(x: T): string {
  return String(x)
}

In this case, we’re saying we’ll take any T and convert it to a string. However, interestingly, this includes null and undefined:

toString(2)
toString("foo")
toString(undefined)
toString(null)

These work by returning "undefined" and "null" - which probably isn’t what we want.

One way to solve this is with the NonNullable utility type1:

total
function totalToString<T extends {}>(x: NonNullable<T>): string {
  return x.toString()
}

Now, when we try to pass undefined or null into our function, Typescript will alert us that they’re not allowed and so we shouldn’t call the function:

totalToString(undefined)
totalToString(null)
totalToString(2)

In this way, we’ve gone from a Partial function, one in which all allowed inputs may not return a value, into a Total function, one which terminates in a value for all possible inputs.

We’ve also done it in a way where we know up front whether we’re passing in undefined or null and can handle that - rather than getting a string of "undefined" that looks just like any other string.

H/t to Lauren Tan for her 2018 DotJS talk Learning To Love Type Systems which introduced me to the NonNullable utility as well as the distinction between Partial and Total functions.

Footnotes

  • 1 Other Utility functions include Partial and Omit which I’ve written about previously as immensely helpful.

Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!