Eliya Cohen
Eliya Cohen

Reputation: 11508

How to declare a type that must be nullable or undefined-able

I want to implement a function that accepts a parameter that might be null or undefined. In other words, if the type can't be null or undefined, then it should throw a compilation error. For example:

type A = string | null; // Valid
type B = CustomInterface | undefined; // Valid;
type C = string; // Invalid;
type D = CustomInterface // Invalid

My use case is for helper functions such as assertDefined, that I don't want them to accept values that will always be defined.

Example (playground link)

function assertDefined<T/*extends ???*/>(value: T) {
    // ...
}

declare let a: A;
declare let b: B;
declare let c: C;
declare let d: D;

assertDefined(a); // Should allow
assertDefined(b); // Should allow
assertDefined(c); // Should not allow
assertDefined(d); // Should not allow

Upvotes: 3

Views: 662

Answers (1)

You can do the trick.


interface CustomInterface {
    foo: number;
}

type Falsy = null | undefined

type A = string | null; // Valid
type B = CustomInterface | undefined; // Valid;
type C = string; // Invalid;
type D = CustomInterface // Invalid


function assertDefined<T>(
    value: T,
    ...nullable: T extends null | undefined ? [] : [never]) {
}


const a: A = Math.floor(Math.random() * 11) <= 5 ? 'a' : null;
const b: B = Math.floor(Math.random() * 11) <= 5 ? { foo: 42 } : undefined;
const c: C = 'c';
const d: D = { foo: 42 }

assertDefined(a); // ok
assertDefined(b); // ok
assertDefined(c); // error
assertDefined(d); // error
assertDefined(d, 2); // still error,

In order to achieve desired behaviour I used rest operator.

It means that, if T can be either null or undefined ...nullable evaluates to empty array, which is mean that there is no second argument. Otherwise nullable evaluates to 1 element array [never].

But is it possible in this case to pass second argument ?

No! You can't do smth like that in TS. never means never :D

So, even if you pass second argument it is still error.

Playground

Drawback:

assertDefined(d, 2 as never); // valid,

Btw, you can find this answer useful. More interesting examples with type negations you can find in my blog which is dedicated to advanced TS types

Upvotes: 4

Related Questions