Null propagating function type annotation

Viewed 132

In this scenario, we have a function which formats a date, but also propagates nulls:

function DateToIso(d: Date | null) {
    return d === null ? null : SugarDate.format(d, '%F');
}

I'd like to be able to call this function and have the return type match the nullability of the provided parameter. For example, this doesn't work – but should in my happy little world.

const nowIso: string = DateToIso(new Date());

How can I specify the function so that the above works, while still allowing propagation of nulls?

2 Answers

You just need to use overloads:

function DateToIso(d: Date): string;
function DateToIso(d: null): null;
function DateToIso(d: Date | null):string | null {
    return d === null ? null : d.toISOString();
}

const withNull = DateToIso(null); // null
const withString = DateToIso(new Date()); // string

Docs

Playground

Btw, you dont need to use explicit types here const nowIso: string = DateToIso(new Date());. TS in most cases is able to infer that

Seems suggestion using overloads does not guarantee null-safety. I suggest to use two different functions and call the corresponding one depending on context:

function DateToIso(d: Date) {
    return SugarDate.format(d, '%F');
}

function DateToIsoNullalbe(d: Date | null) {
    return d === null ? null : DateToIso(d);
}

Maybe you decide to use a wrapper to lift any non-nullable function to nullable. Something like

function wrapNullable<T, R>(f: (t: T) => R): (t: T | null) => (R | null) {
    return t => t === null ? null : f(t);
}

So you could do wrapNullable(DateToIso)(xxx)

Playground Link

I guess it's even better as it does not have null check condition when it's known you don't need them. May improve performance... theoretically at least.

Related