javascripttypescriptoptional-chaining

Optional chaining on function arguments


If a function f: (X => Y) | undefined is possibly undefined, and x: X is defined, then we can use optional-chaining operator ?. to apply f to x:

f?.(x)          // ok, no problem if `f` is undefined

But when f: X => Y is defined and x: X | undefined is possibly undefined, there does not seem to be any syntax to "map" the f over the "optional" x:

f(?.x)          // not valid syntax; Unclear what to do when `x` is undefined

I could try to implement pipeTo to swap the order of f and x, and then make it work with ?. again, as follows:

function opt<X>(x: X | undefined): { pipeTo<Y>(f: (a: X) => Y): Y } | undefined {
    return typeof x === 'undefined' ? undefined : {
        pipeTo<Y>(f: (a: X) => Y): Y {
            return f(x)
        }
    }
}

which I could then use as opt(x)?.pipeTo(f), for example:

function square(n: number): number { return n * n }

for (const x of [42, undefined, 58]) {
  console.log(opt(x)?.pipeTo(square))
}

Is there any less cumbersome standard solution for applying a certainly existing f to a possibly undefined x?


Clarification: "cumbersome" := anything that forces me to write down the subexpression x twice, or to clutter the scope with meaningless helper-variables.


Solution

  • It's like you're looking at the Nullable<T> type operation (defined as

    type Nullable<T> = T | null | undefined
    

    ) as a functor and want to perform the functor fmap action on it, to turn a function f of the form (x: X) => Y into another function of the form (x?: Nullable<X>) => Nullable<Y>. I don't think there's any built-in functionality which behaves this way, nor could I speak authoritatively about its presence in third-party libraries, but you could write it yourself easily enough:

    const fmapNullable = <X, Y>(f: (x: X) => Y) =>
        (x?: Nullable<X>): Nullable<Y> => x == undefined ? undefined : f(x);
    

    and then use it:

    function square(n: number): number { return n * n };
    
    for (const x of [42, undefined, 58]) {
        console.log(fmapNullable(square)(x)?.toFixed(1))
        // "1764.0";
        // undefined;
        // "3364.0"
    }
    

    Syntactically I don't think you can get a notation as terse as the optional chaining operator (?.); TypeScript isn't Haskell, after all. You could shorten "fmapNullable", but you'll still be applying a function, so at best you'd have something like $_$(square)(x). Oh well!

    Playground link to code