Utilizing optional chaining with function parameters

When dealing with a function f: (X => Y) | undefined that may be undefined, and x: X is defined, we can utilize the optional chaining operator ?. to apply f to x:

f?.(x)          // This is fine even if `f` is undefined

However, if f: X => Y is defined and x: X | undefined could potentially be undefined, there doesn't seem to be a clear syntax for "mapping" f over the "optional" x:

f(?.x)          // This syntax is not valid; It's ambiguous what to do when `x` is undefined

One approach could be implementing a function called pipeTo, which changes the order of f and x, allowing it to work with ?. 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)
        }
    }
}

This can then be used as opt(x)?.pipeTo(f). For instance:

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

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

Is there a simpler standard solution available for applying a definite f to a potentially undefined x?


To clarify: "cumbersome" refers to anything that requires writing down the subexpression x twice or adding meaningless helper variables to the code.

Answer №1

It's as if you're observing the Nullable<T> type operation (defined as

type Nullable<T> = T | null | undefined

) in a similar way to a functor and wish to execute the functor fmap action on it, transforming a function f of the format (x: X) => Y into another function of the format

(x?: Nullable<X>) => Nullable<Y>
. It seems there isn't a pre-existing feature that behaves this way, and I cannot definitively confirm its existence in third-party libraries either. However, you can easily create it yourself:

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

and then implement 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"
}

Syntax-wise, a notation as concise as the optional chaining operator (?.) may not be achievable; TypeScript is not Haskell, after all. You might abbreviate "fmapNullable", but you will still be applying a function, resulting in something like $_$(square)(x) at best. That's just how it goes!

Check out the code on the Playground

Answer №2

Looks like you're dealing with an array, so here is a unique implementation for handling arrays:


function checkNotNull<T>(val: T | null | undefined): val is T {return val != null}

class ArrayWithOneOrZero<T> extends Array<T> {
    transform<V>(mapper: (val: T) => V) {
        return this.map(mapper).filter(checkNotNull)
    }
    eject(): T | undefined {
        return super.pop();
    }
    static createFrom<T>(element?: T | null | undefined): ArrayWithOneOrZero<T> {
        let arr = new ArrayWithOneOrZero<T>();
        if (element != null) arr.push(element);
        return arr;
    }
}
function optionalValue<T>(val: T | null | undefined) {return ArrayWithOneOrZero.createFrom(val)}

function calculateSquare(num: number): number { return num * num }

for (const item of [42, undefined, 58]) {
  console.log(
    optionalValue(item).transform(calculateSquare).eject()
  )
}

Answer №3

At some point in time, someone out there must have thought about monkey-patching Function:

Function.prototype.$ = function (x) {
  return typeof x === 'undefined' ? undefined : this.apply(null, [x])
}

It appears to be functional, with only a minimal two-character syntactic overhead:

function square(x) { return x * x }
square.$(42)        // 1764
square.$(undefined) // undefined

Please refrain from doing this. Just because we have the ability doesn't mean it's the right thing to do.

(Also, it's written in JS, as I don't currently feel like trying to convert it for TypeScript)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the proper method for assigning a value to a variable within a JSON

While utilizing the js.cookie.js library available on Github, I encountered a challenge when attempting to set a JSON cookie. According to the documentation for js.cookie.js, in order to set a JSON cookie, the following syntax should be used: Cookies.set( ...

AngularJS has encountered an issue with a route resolve promise that has not been completely resolved

I'm currently working on a simple task to manage user roles within routes. The goal is straightforward: Verify the role of the logged-in user on each route (using a resolve function to authenticate the user based on token or login credentials) Direc ...

What is the process for subtracting data from an object in a Node.JS environment?

My task involves analyzing sensor data which is stored as an array of objects containing current and previous month's information. The goal is to calculate the difference between the current and previous month's data and add this difference to th ...

Creating Tables with Horizontal Headers using drawHeaderRow in Typescript

Utilizing jsPDF, I was able to generate a table from JSON data and store it in a PDF document. To showcase this functionality, I developed an Angular2/Typescript application. This application creates a table based on my JSON data. My goal now is to use jsP ...

New to React: Arrow Function Example that Might Puzzle You

As a beginner in React/ES6, I came across this code snippet for handling a checkbox within a custom component that contains a material-ui CheckBox. My goal is to expand the custom component by adding more fields, such as a textbox where users can provide a ...

What is the best method for initializing a grid combo box value using JavaScript?

How do I set the value of a combobox in a grid itemtemplate using JavaScript? <telerik:GridTemplateColumn AutoPostBackOnFilter="true" CurrentFilterFunction="Contains" DataField="FAULT" FilterControlWidth="100%" ...

Is it possible for the JavaScript code to cease execution once the tab is closed?

I am working on a JavaScript code snippet that is designed to execute once a component finishes loading: function HelloThere() { React.useEffect(() => { setTimeout(() => { // code to make a server call to write data to DB ...

Using React to Filter cards according to the selected value from an Autocomplete feature

I'm currently developing a React application that utilizes Material-UI's Autocomplete component to filter cards based on selected car brands. These cards represent various models, and the goal is to update them when a user picks a brand from the ...

Activate the child for an update

Welcome! I am a newcomer to Angular and would greatly appreciate any assistance. The parent component of my picker has the ability to create various rules for each option. However, these rules are dynamic and can change frequently. I need to ensure that ...

Using experimental.externalDir in NextJS prevents the use of absolute imports in external libraries

In my monorepo setup with nextjs, lerna, and npm workspaces, the folder structure is as follows: packages next-js-app pages index.tsx tsconfig.json ui-library src components dropdown. ...

What are the various undisclosed schema types in A-Frame?

I've been exploring different examples of property types in the official documentation and various Github repositories (though now I can't remember which ones). The latter introduced me to unique properties like "min" and "max" for numbers, as we ...

Utilizing Angular's $locationProvider.html5Mode in conjunction with $window parameters

Lately, I encountered some difficulties with Google indexing due to angular routing. After much trial and error, I discovered that using $locationProvider.html5Mode solved the issue. However, a new problem has arisen where $window variables lose their val ...

AngularFire2 Firestore Custom Query: retrieve documents based on current date and time.startTime

Welcome to the world of AngularFire2 and Firestore! My objective is clear: Query data from Firestore where startTime matches currentDateRange. I am facing some challenges with creating a dynamic query in Firestore. After going through the official docume ...

How do I make the dropdown menu in a navbar stretch horizontally to cover the entire width of the page container?

When I hover over an item in the navbar, a horizontal dropdown menu currently opens as shown below (note the width of the dropdown menu): https://i.sstatic.net/824OVkWT.png What am I aiming for? I want to expand the horizontal dropdown menu like in the i ...

The data-tooltip feature displays information as [Object object]

There seems to be an issue with displaying text in the data-tooltip. Instead of showing the intended message, it is displaying [Object object]. import React from "react"; import { FormattedMessage } from "react-intl"; const translate = (id, value={}) = ...

Vue 3 select component with first character filtering functionality

Is there a way to filter options based on user input in Select2, especially when I need to get the first symbols entered by the user? I have tried using the @select event but it doesn't seem suitable for this task. How can I achieve this? <Select2 ...

Display the Vaxis line in a bar graph using Google Chart

Currently, I am involved in a project where I need to display a graph that resembles the one depicted here. To accomplish this task, I am utilizing the Google Visualization Chart API. Successfully, I have managed to generate the chart as illustrated below ...

`Sending binary data to client through GraphQL: A comprehensive guide`

I have a GraphQL server running on express. I am looking for a way to send images back to the client using nodejs buffer objects instead of JSON. How can I configure my graphql server to return bytes directly, without encoding them in base64 due to large ...

What are some possible applications of conditional hooks within a React.Component class?

According to the documentation, it is stated that Hooks cannot be utilized within class components. However, there are workarounds using higher order components available as explained in this Stack Overflow post: How can I use React hooks in React classic ...

Setting the default value in a Select tag using React

Within this particular Select tag, the goal is to establish an initial value and assign that value to a variable named name. The Select tag itself includes the following: <Field name="data.account" render={({ field: { name, ...restFieldProps } }) = ...