Exploring TypeScript's Monads with Generic Type Parameters

Exploring the concept of monads and using TypeScript for better comprehension is my current focus.

Here is what I am aiming to achieve:

type Bind<M, A, B> = (ma: M<A>) => (a_mb: ((a: A) => M<B>)) => M<B>
type Unit<M, A> = (a: A) => M<A>

Encountering these errors in TypeScript:

error TS2315: Type 'M' is not generic.
type Bind<M, A, B> = (ma: M<A>) => (a_mb: ((a: A) => M<B>)) => M<B>
                          ~~~~

error TS2315: Type 'M' is not generic.
type Bind<M, A, B> = (ma: M<A>) => (a_mb: ((a: A) => M<B>)) => M<B>
                                                     ~~~~

error TS2315: Type 'M' is not generic.
type Bind<M, A, B> = (ma: M<A>) => (a_mb: ((a: A) => M<B>)) => M<B>
                                                               ~~~~

error TS2315: Type 'M' is not generic.
type Unit<M, A> = (a: A) => M<A>
                            ~~~~

Is there a way to specify that type parameter M should be generic or is there an alternative approach?

The goal is to substitute, for example, Maybe instead of M later on, with Maybe being a flexible type:

type Maybe<T> = { kind: 'nothing' } | { kind: 'some', value: T }

Answer №1

Unfortunately, TypeScript currently does not provide built-in support for higher kinded types needed to define concepts like Bind or Unit. There has been a long-standing feature request open on microsoft/TypeScript#1213, but it remains uncertain whether this functionality will be implemented in the future.

The main issue lies in TypeScript's limitation to abstract over type functions, which are essential for manipulating types similar to how regular functions operate on values. While TypeScript can represent specific concrete type functions such as Array, the abstraction and use of type functions in general is not supported. This impediment restricts the ability to pass around type functions to other higher-order functions, ultimately preventing direct implementation of certain functionalities that rely on arbitrary type functions as parameters.


To highlight, TypeScript doesn't offer direct support for higher kinded types. Though there are ways to emulate them, the existing methods often involve cumbersome workarounds and significant manual input to overcome TypeScript's limitations in this regard. Hence, while it's feasible to mimic higher kinded types, the process may not qualify as straightforward or intuitive.

If looking for alternatives, exploring libraries like fp-ts could prove beneficial. As an illustration, a basic implementation approach could entail:

interface TypeFunction<T> { }
type TypeFunctionName = keyof TypeFunction<any>
type Apply<F extends TypeFunctionName, T> = TypeFunction<T>[F];

For each targeted type function in scope, modifications within TypeFunction<T> are necessary. For example:

interface TypeFunction<T> {
  Array: Array<T>
}

Additionally,

interface TypeFunction<T> {
  Foo: Foo<T>
}

Following these adjustments, references previously made with M<T> require update to

Apply<M, T></code—using the key assigned to the respective type function within <code>TypeFunction<T></code. Consequently, expressions like <code>Apply<"Array", string>
would yield string[], whereas
Apply<"Foo", string>
would produce {x: string}.


In light of this outline, you can proceed to devise implementations for Bind, Unit, and more as needed:

type Bind<M extends TypeFunctionName> =
  <A>(ma: Apply<M, A>) => <B>(a_mb: ((a: A) => Apply<M, B>)) => Apply<M, B>;
type Unit<M extends TypeFunctionName> =
  <A>(a: A) => Apply<M, A>;

(Note the revised scope for A and

B</code... ensuring compatibility across all instances). Supporting operations like <a href="https://en.wikipedia.org/wiki/Functor_(functional_programming)" rel="nofollow noreferrer">fmap</a> becomes achievable too:</p>
<pre><code>const fmap = <M extends TypeFunctionName>(
  bind: Bind<M>, unit: Unit<M>) => <A, B>(f: (a: A) => B) =>
    (ma: Apply<M, A>) => bind(ma)(a => unit(f(a)))

As a playful exercise, implementing bind and unit for the Array and Foo monads could unfold like:

namespace ArrayMonad {
  const bind: Bind<"Array"> = ma => mf => ma.flatMap(mf);
  const unit: Unit<"Array"> = a => [a];

  // Additional manipulations can follow...
}

namespace FooMonad {
  const bind: Bind<"Foo"> = ma => mf => mf(ma.x);
  const unit: Unit<"Foo"> = a => ({ x: a });

  // Extra steps can be included...
}

The provided structure appears reasonable overall; however, minor quirks like specifying

Bind<"Foo"></code instead of <code>Bind<Foo></code reflect the current constraints faced. Further exploration into managing constructs like <code>Maybe
might lead to interesting insights.

Feel free to experiment with the code using the TypeScript Playground!

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

The assigned type does not match the type 'IntrinsicAttributes & { children?: ReactNode; }'. This property is not assignable

I have been struggling to resolve this issue, but unfortunately, I have not found a successful solution yet. The error message I am encountering is: Type '{ mailData: mailSendProps; }' is causing an issue as it is not compatible with type &apos ...

Examining the reasoning behind comparisons within an Ionic view

When looping through the results in an ion-list, I compare the values dynamically by using Angular's ngIf directive. <ion-list> // Loop the results <ion-item *ngFor="let protocole of protocoles"> <ng-template [ngIf]="{{value}} == ...

The page is unable to find the 'backdrop' property because it is undefined

I'm currently using this code in my HTML page. In my angular module, I have imported all the necessary components. However, I keep encountering an error related to the backdrop. Can someone please assist me with this issue? I am unfamiliar with backdr ...

Navigating Dynamically between tabs - A How-to Guide

I am working on a mat-tab Angular app where I need to dynamically generate links and transfer them to a navLinks object. Despite ensuring that the concatenation is correct, it seems like my approach is not working as expected. Here's a glimpse of what ...

Guide on integrating react-tether with react-dom createPortal for custom styling of tethered components based on their target components

Within a Component, I am rendering buttons each with its own tooltip. The challenge is to make the tooltip appear upon hovering over the button since the tooltip may contain more than just text and cannot be solely created with CSS. The solution involves ...

Utilizing generic union types for type narrowing

I am currently attempting to define two distinct types that exhibit the following structure: type A<T> = { message: string, data: T }; type B<T> = { age: number, properties: T }; type C<T> = A<T> | B<T>; const x = {} as unkn ...

Navigating back to the login page in your Ionic V2 application can be achieved by utilizing the `this.nav

Running into an issue with navigating back to the login screen using Ionic V2. Started with the V2 tabs template but added a custom login page, setting rootPage = LoginPage; in app.components.ts. When the login promise is successful, I used this.nav.setR ...

Is there a way in Typescript to filter for the first instance of a unique object in an array of objects?

Having an array of JSON objects like this, the task is to iterate through it and retrieve the first occurrence of 'appname', such as 'app1' or 'app2', and store the entire object for each... myArray[ { ...

Dealing with null exceptions in Angular 4: Best practices

Hi there, I am encountering an issue with binding my model data to HTML fields where when I try to edit the data it returns an error saying "cannot read value of null". How can I resolve this? Here is the HTML code snippet: <div class="form-group"> ...

Discover the magic of integrating FeathersJS REST functionality within Angular with these simple steps

I've encountered a challenge while trying to make Feathers work in Angular with a Feathers REST server. It seems that no requests are being made. My Feathers server hosts the resource http://example.com/app/experiences which returns data in paginated ...

Incorporating TypeScript into a project that already contains .js files

I currently have a NodeJS project with .js files written in ES6. I am interested in integrating typescript into this existing project. What steps should I follow? Can I simply introduce .ts files within the same directory structure? One issue I have encou ...

Utilizing Express, Request, and Node.js to manage GET requests in typescript

I'm struggling with using Express and Request in my project. Despite my efforts, the response body always returns as "undefined" when I try to get JSON data from the server. In my server.js file, this is the code snippet I have been working on: impo ...

What is the best way to search for an Enum based on its value?

One of my challenges involves an enum containing various API messages that I have already translated for display in the front-end: export enum API_MESSAGES { FAILED_TO_LOAD = 'Failed to load data', TOKEN_INVALID = 'Token seems to be inva ...

Uncovering the Secrets of Typescript Mixins: Leveraging Shared Properties Across Combined Classes

I am currently exploring the concept of mixins as explained in the TypeScript documentation. https://www.typescriptlang.org/docs/handbook/mixins.html You can find a playground setup here. My question revolves around defining functions like jump() and du ...

Add a class to a button in an Angular btn-group if a specific string is found within an

I am currently working on a project where I have multiple buttons that need to toggle an active class when selected in order to change their color. Below is a snippet of what I have: In the array called 'selected', I have: this.selected = [&ap ...

Customizing your Mui theme with Typescript may lead to unexpected errors

Within my MUI theme, I am aiming to customize the link element as shown below: components: { MuiLink: { defaultProps: { component: LinkComponent, }, }, } However, I encountered the following TypeScript error: Type error: Ty ...

Is it possible for TypeScript to automatically detect when an argument has been validated?

Currently, I am still in the process of learning Typescript and Javascript so please bear with me if I overlook something. The issue at hand is as follows: When calling this.defined(email), VSCode does not recognize that an error may occur if 'email ...

What types should be used when passing a NgRx Action as a parameter to a function?

Within my Effects function, I have implemented the following code structure. I have included a few lines of code for the catchError block to ensure that: Any errors are handled by the state/store The errors are forwarded to the global error handler / Int ...

Use TypeScript to selectively return a portion of the interface

I am faced with a User interface structured as follows interface IUser { name: string, phoneNumber: string } and another interface called PublicUser structured like this interface IPublicUser { name: string } The goal is to extract only publ ...

The Power of Angular 2's Reactive Form Validation

I am currently developing a custom validator for a group of inputs within my dynamic form. this.transitionForm = this.fb.group({ effectiveStartDate: [this.utils.dateToISO(startDate), Validators.compose([Validators.required, this.validateDates])], effe ...