TypeOfNaN

Making Every Object Property Nullable in Typescript

Nick Scialli March 18, 2021🚀 3 minute read

I find it interesting which utility types the Typescript languages has adopted and which it hasn’t. For example, NonNullable is a built-in utility type but Nullable is not.

In this post, we’ll quickly write our own Nullable utility type that will allow any object propert to be nullable.

What We’re Trying to Do

A quick example of what we’re trying to do: let’s say we have a Person type:

type Person = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

But perhaps we have an interface that we know might give us any subset of these object properties as null, so we’d like to quickly derive a type like this:

type NullablePerson = {
  name: string | null;
  email: string | null;
  age: number | null;
  admin: boolean | null;
};

We might hope that Typescript has a built-in Nullable type but, to our dismay, it doesn’t!

Rolling Your Own Nullable Utility Type

Fortunately, a Nullable utility type is only one line long:

type Nullable<T> = { [K in keyof T]: T[K] | null };

And the way we might use it with our Person type is like this:

const nullablePerson: Nullable<Person> = {
  name: 'Daffodil',
  email: null,
  age: null,
  admin: true,
};

One Step Further: Deeply Nullable

Perhaps we have a use case where we want our nullable type to be deep, meaning that any number of levels down the object should be nullable.

Let’s modify our Person type to have another level of properties:

type Person = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
  dog: {
    name: string;
    age: number;
  };
};

To make this deeply nullable, we can simple recursively call the Nullable type on its own properties:

type DeepNullable<T> = {
  [K in keyof T]: DeepNullable<T[K]> | null;
};

And now if we implement this on our new Person type, we can see that our object can be nullable all the way down.

const nullablePerson: DeepNullable<Person> = {
  name: 'Daffodil',
  email: null,
  age: null,
  admin: true,
  dog: {
    name: 'Fido',
    age: null,
  },
};

Now we can use this utility type wherever we need and, with any luck, Typescript may add it as a built-in type someday!

Nick Scialli is a software engineer at the U.S. Digital Service.