Discovering the Type Inference of Function Composition Method (Chaining) in TypeScript

I'm working on implementing a function that can add a chainable method for function composition. Here's what I have so far:

Also see: TypeScript playground

{
  const F = <T, U>(f: (a: T) => U) => {
    type F = {
      compose: <V, >(g: (b: U) => V) => (((a: T) => V) & F);
    };
    return Object.defineProperty(f, "compose", {
      configurable: true,
      value: <V,>(g: (b: U) => V) => F((a: T) => g(f(a)))
    }) as ((a: T) => U) & F
  };
  //--------------------------------
  const f1 = (a: number) => a + 1;
  const f2 = (a: number) => a.toString();
  const identity = <T,>(a: T) => a;
  //--------------------------------
  const F2 = F(f2);
  // ((a: number) => string) & F  // good
  const F12 = F(f1).compose(f2);
  // ((a: number) => string) & F  // good
  //--------------------------------
  const F2i = (F2).compose(identity);
  // ((a: number) => string) & F  // good
  const f12i = (F12).compose(identity);
  // ((a: number) => number) & F  // bad  why??
  //--------------------------------
  const fi1 = F(identity).compose(f1);
  /* ts(2345) error
    Argument of type '(a: number) => number' is not assignable to parameter of type '(b: unknown) => number'.
        Types of parameters 'a' and 'b' are incompatible.
          Type 'unknown' is not assignable to type 'number'.
    const f1: (a: number) => number
  */
}

In this example code, we have three basic functions; f1, f2, and identity.

F is the function that extends a specified function with a method for function composition.

I've made it work somehow, but I've encountered at least two issues.

1.

When using F for f2, the type of F2 (

((a: number) => string) & F
) is as expected. Similarly, when using F for f1 and composing with f2, the type of F12 is also as expected.

However, the type of (F12).compose(identity) turns out to be

((a: number) => number) & F
, which is unexpected. I've been trying to figure out why this happens without success.

If you have any advice, I'd appreciate it. Thanks!

EDIT:

Please note that the functions should not be wrapped in an Object, and my goal is to provide a compose method directly to the functions:

 const f = (a: number) => a + 1;
  const fg = f.compose(g);

  //not
  {
    f: f,
      compose:someFunction
  }

EDIT: Regarding issue #2, I created a separate question based on comments from @jcalz:

Is there any workaround for ts(2345) error for TypeScript lacks higher kinded types?

2.

I'm encountering a ts(2345) error, and the error message is unclear to me, making it difficult to resolve. Any ideas on how to fix this would be greatly appreciated.

Answer №1

If you take a closer look at the type of const F, you will notice an issue when you expand it:

const alsoF: <T, U>(f: (a: T) => U) =>
  ((a: T) => U) & {
    compose: <V>(g: (b: U) => V) => (((a: T) => V) & {
      compose: <V>(g: (b: U) => V) => ...

The problem arises because each call to compose() is expecting a callback parameter of type U, while you actually want it to be of type

V</code, which is the return type of the previous <code>compose()
.

To resolve this, we need to use generics in TypeScript to keep track of both the original type and the current type. The updated definition of F<T, U> would help in maintaining this relationship:

interface F<T, U> {
  (a: T): U;
  compose<V>(g: (b: U) => V): F<T, V>;
}

By implementing this concept, we can now accurately reflect the type of the callbacks passed to compose() for proper chaining.


This approach ensures that the returned types are aligned with expectations. Feel free to experiment further using the code provided!

Try out the updated implementation on TypeScript Playground here

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

An easy method to define argument types for every function type in TypeScript

How can I assign argument types to each function type in TypeScript? Each function has a parameter associated with it. [Example] type F1 = (arg: number) => void; type F2 = (arg: string) => void; const caller = (f: F1 | F2) => (n: number | strin ...

Whenever I attempt to execute yarn build within next.js, an error always seems to occur

When attempting to compile my next.js project using the yarn build command, an error consistently occurs: Error: Export encountered errors on following paths: /settings at D:\web3\futnft\frontend\node_modules\next\ ...

Cloud Formation from CDK doesn't pause for addDependency to finish

I'm currently in the process of building a CDK stack and I am fairly new to CDK. My goal is to create a Simple Email Service (SES) ConfigurationSet followed by an EmailIdentity. The issue I encountered is that the creation of the EmailIdentity fails d ...

Sequencing API Calls: A Guide on Making Sequential API Requests

Currently, I am working on mastering RxJS. Within my project, there are 3 API calls that need to be made. Specifically, I must execute the 2nd API call and then pass its data as a parameter to the 3rd API call. My attempt at achieving this functionality is ...

The credentials in AWS S3Client are failing to load correctly

I encountered an issue with the S3 Client from aws sdk v3: When using the S3Client as outlined in the documentation and providing credentials via environment variables, I received an error message stating The AWS Access Key Id you provided does not exist ...

One can only iterate through the type 'HTMLCollection' by utilizing the '--downlevelIteration' flag or setting a '--target' of 'es2015' or above

I'm currently working on developing a loader for my static grid. I've incorporated the react-shimmer-skeleton package source code, but I'm encountering issues with eslint in strict mode. You can find the respective repository file by followi ...

The TypeScript compiler is unable to locate the module react-scripts within the lerna webpack configuration

Recently, I've been working on setting up a new project using lerna, react-scripts, webpack, and sass. Here is my current directory structure: myApp /packages /myReactApp -> a react create app application /tsconfig.json /package ...

Transporting a Typescript file to the customer using JavaScript

As a newcomer to typescript and in the process of creating a small application in visual studio 2013, I have noticed that when viewing the project in Chrome's developer tools, typescript files (*.ts) are being downloaded to the client. This could pote ...

RxJs Subject: Acquiring the Sender

I have been working with Subjects and there is a .subscribe() in a specific class. Emitting values to this class from different other classes has resulted in the subscribe function being triggered multiple times, yet I am unsure of where these emits are co ...

Next.js 14 useEffect firing twice upon page load

Having an issue with a client component in next js that is calling an API twice at page load using useEffect. Here's the code for the client component: 'use client'; import { useState, useEffect } from 'react'; import { useInView ...

Converting a string to a number in an Angular template

Within my select box, I am capturing the selected value and binding it to the variable startingYear. However, I need the type of startingYear to be a number, but it is currently registering as a string. Is there a way to convert it to a number? console ...

Achieving Bi-Directional Data Binding with Vue.js using the vue-property-decorator Library

Currently, I am working with Vue.js and TypeScript while also utilizing the vue-property-decorator. My goal is to establish two-way data binding between a parent and child component. Below is an example of what I have in mind: Parent Component <templa ...

Building a customizable class from scratch

I am currently working on developing configurable classes that come with default values, but allow for configuration changes if needed. The concept involves creating an instance of a class by calling its type specified in the static properties of the Test ...

Obtain the file path relative to the project directory from a Typescript module that has been compiled to JavaScript

My directory structure is as follows: - project |- build |- src |- index.ts |- file.txt The typescript code is compiled to the build directory and executed from there. I am seeking a dependable method to access file.txt from the compiled module without ...

Angular 5 - Creating a dynamic function that generates a different dynamic function

One of my latest projects involved creating a versatile function that generates switch-case statements dynamically. export function generateReducer(initialState, reducerName: ReducerName, adapter: EntityAdapter<any>): (state, initialState) => ISt ...

How should I properly initialize my numeric variable in Vue.js 3?

Encountering an issue with Vue 3 where the error message reads: Type 'null' is not assignable to type 'number'. The problematic code snippet looks like this: interface ComponentState { heroSelected: number; } export default define ...

Utilizing the onCLICK event handler with numerous parameters

I'm in need of assistance with creating a function that involves multiple variables, preferably at least two... The table I am working with was generated using PHP and MySQL, all IDs were dynamically created which is why I am looking for a way to cap ...

How does TypeScript interpret the data type 'a' | 'b' as a string?

As a beginner in TypeScript, React, and English, I apologize for any confusion. I have written a function like this: interface ObjectAttrUniqueState { editVisible: boolean; currentId: number; selectedUnique: number[]; name: string; desc: string; ...

PHP version 7.2 does not support the pcntl_fork function

When running PHP 7.2.0RC1 on macOS from php-osx.liip.ch, the code snippet provided below is returning false: function_exists('pcntl_fork') Even though the PHP CLI used to execute the script indicates that pcntl is enabled (php -i) and it is not ...

Easy steps for importing node modules in TypeScript

I'm currently navigating the world of TypeScript and attempting to incorporate a module that is imported from a node module. I have chosen not to utilize webpack or any other build tools in order to maintain simplicity and clarity. Here is the struct ...