Is it possible to make functions dynamically adapt their behavior based on the types of arguments passed to them?

Consider this intricate collection of functions:

    interface Succes<a> {
      kind: 'succes'
      value: a
    }
    interface Failure<e> {
      kind: 'failure'
      error: e
    }

    type Result<a, e> = Succes<a> | Failure<e>

    const unit = <a>(a:a): Succes<a> => ({kind: 'succes', value: a})
    const fail = <e>(e:e): Failure<e> => ({kind: 'failure', error: e})

    interface fun<a, b> { (a: a): b }

    const map = <a,b, e>(f: fun<a, b>): fun<Result<a,e>, Result<b,e>> => r => 
      r.kind == 'succes' ? unit(f(r.value)) : r

    const join = <a, e>(r:Result<Result<a, e>, e>): Result<a, e> => 
      r.kind == 'failure' ? r : r.value

    const then = <a, b, e>(f:fun<a, Result<b, e>>) => (r:Result<a, e>) => 
        join(map(f)(r))

    const railSystem = <a, e>(r: Result<a, e>) => ({
      map: <b>(f: (a:a) => b) => railRoad<b, e>(map<a, b, e>(f)(r)),
      then: <b>(f: (a:a) => Result<b,e>) => railSystem(then(f)(r))
    })

Focus on the railSystem section, which allows us to structure our code like this:

railSystem<User, *possibleErrors*>(getUser())
  .then(formatName)
  .map(greet)

This approach is promising as it handles errors throughout multiple function calls, but we must define the potential errors in advance. Could we dynamically determine the error types based on subsequent .then or .map functions?

Is it possible for the e in railSystem to adjust according to the signature of chained functions passed through .then or .map? This would allow the original railSystem function to infer the error type.

An interactive TypeScript demonstration can be accessed here

Answer №1

Following are the TS type parameter naming conventions where capital letters are used, and all instances of "succes" have been corrected to "success". These changes are purely cosmetic and can be disregarded. The convention of referring to "success" types as A and B, and to "failure" types as E and F will be maintained throughout. Even though using F for an error type may not be ideal, it aligns alphabetically with A/B.


The main issue likely lies in the fact that your then() function and railRoad() function do not include a mechanism for expanding the error type. One approach to address this within then() is provided below:

const then = <A, B, F>(f: fun<A, Result<B, F>>) => <E>(r: Result<A, E>) =>
  join(map<A, Result<B, E | F>, E>(f)(r))

In this snippet, the initial function requires something that converts an A into a Result<B, F>. It then takes a Result<A, E> and produces a Result<B, E | F>. Combining E and F into E | F is crucial for facilitating correct inferences later on.


A secondary concern arises from your railRoad() function utilizing the higher order generic type inference feature introduced in TypeScript 3.4, resulting in poor compiler performance due to the inferred type becoming recursive, like

{map: () => {map: () => ....
. To tackle this, an interface named RailRoaded<A, E> has been created to represent the return type of railRoad():

interface RailRoaded<A, E> {
  map<B>(f: (some: A) => B): RailRoaded<B, E>;
  then<B, F>(f: (some: A) => Result<B, F>): RailRoaded<B, E | F>;
}

const railRoad = <A, E>(r: Result<A, E>): RailRoaded<A, E> => ({
  map: <B>(f: (some: A) => B) => railRoad<B, E>(map<A, B, E>(f)(r)),
  then: <B, F>(f: (some: A) => Result<B, F>) => railRoad(then(f)(r))
})

Specifying the return type of railRoad as

RailRoaded<A, E></code significantly boosts performance, as it directs the compiler to simply verify compatibility without synthesizing a new return type. You can observe how the <code>then()
method of a RailRoaded<A, E> generates a failure type amalgamated with unions.


That sums up the key points. On applying this setup, the results look promising. Let's break down the process during execution:

const chooChoo = railRoad(getUser()).then(formatName).map(greet);
// const chooChoo: RailRoaded<string, "no-user" | "no-name">

Initial inspection seems positive. For further clarity, let's analyze the inferences at each stage:

const engine = railRoad(getUser());
// const engine: RailRoaded<User, "no-user">
const car = engine.then(formatName);
// const car: RailRoaded<string, "no-user" | "no-name">
const caboose = car.map(greet);
// const caboose: RailRoaded<string, "no-user" | "no-name">

Everything appears to be on track. Trust this breakdown aids in understanding; good luck!

Link to Playground for code visualization

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

Troubles with Angular Form Elements: Bridging the Gap Between AbstractControl and FormControl

Encountering an error when trying to use a form with controls. Type 'AbstractControl' is missing the following properties from type 'FormControl': registerOnChange, registerOnDisabledChange, _applyFormState Check out Form Code th ...

Using TypeScript to Import Modules without Default Exports (CommonJS)

Can a module that is defined without a default export be imported using import module from 'module'; and then compiled to commonjs? An answer on Stack Overflow suggests that it might be possible with the use of the --allowSyntheticDefaultImports ...

Tips for creating a state change function using TypeScript

Imagine the temperature remains constant for 20 minutes, but at the 21st minute, it changes. The state change is determined by a programmable state change function. How can I create a function to calculate the difference in state change? if(data.i ...

Tips for adjusting the angle in SVG shapes

I have designed an svg shape, but I'm struggling to get the slope to start from the middle. Can someone please provide assistance? <svg xmlns="http://www.w3.org/2000/svg" fill="none"> <g filter="url(#filter0_b_1_2556)"&g ...

The 'event' parameter is being implicitly assigned an 'any' type

I'm currently utilizing TypeScript within an Electron application but I am encountering the following error message: Parameter 'event' implicitly has an 'any' type.ts(7006) Below is the code snippet in question. Any suggestions ...

Is implementing client components in Server Side pages an effective strategy for optimizing SSR performance?

In order to overcome the challenge of using client-side components in server-side pages, I made the decision to create a client-side wrapper to encapsulate these components within server-side pages. This way, I can manage all API calls and data fetching on ...

What is the best way to leverage TypeScript for destructuring from a sophisticated type structure?

Let's consider a scenario where I have a React functional component that I want to implement using props that are destructured for consistency with other components. The component in question is a checkbox that serves two roles based on the amount of ...

Issue encountered while attempting to export JSON data to Excel using XLSX in Angular 8

I am having trouble exporting an array of objects to an Excel sheet using the xlsx library in Angular 8. Below is a snippet of my code that attempts to export JSON data to Excel with multiple sheets. Here's how it looks inside my app.html file: < ...

Troubleshooting problem with @Input number in Angular 2

Here is the component in question: <component value="3"></component> This is the code for the component: private _value:number; get value(): number { return this._value; } @Input() set value(value: number) { console.log(v ...

The asyncData and fetch functions are failing to populate the data

I am currently working with nuxt v2.14.1 along with typescript and the nuxt-property-decorator package. I've been encountering a variety of issues. One of the problems I'm facing is the inability to set data from fetch() or asyncData. console. ...

Limit Typescript decorator usage to functions that return either void or Promise<void>

I've been working on developing a decorator that is specifically designed for methods of type void or Promise<void>. class TestClass { // compiles successfully @Example() test() {} // should compile, but doesn't @Example() asyn ...

Tips for creating an array that aligns with the keys of a type in TypeScript

Currently, I am utilizing the Kysely SQL builder for JS based on Vercel's recommendation, despite the limited documentation and community support. This SQL builder is fully typed, allowing you to create a db object with a schema that recognizes table ...

How can Angular CLI prevent the submission of an HTML form unless a previous option has been selected

I am currently working on a form that contains select fields with various options pre-populated. <form [formGroup]="selectVehicleForm"> <select formControlName="Manufacturer"> <option *ngFor='let ...

How to Manage NavBar Back Button in Ionic Framework?

Various methods have been proposed to manage the action of going back using the hardware button in Ionic. One common approach is shown below: platform.ready().then(() => { platform.registerBackButtonAction(() => { However, I am interested in fin ...

Error thrown when using React Router DOM: FC{} | ReactNode is not compatible with type ReactNode

Recently, I started using TypeScript and delving into a project that involves the react-router-dom. However, as I attempt to create elements in my App.tsx file, an error keeps popping up. Let's take a look at the code snippet: <Route path="la ...

Conditional type/interface attribute typing

Below are the interfaces I am working with: interface Movie { id: number; title: string; } interface Show { title: string; ids: { trakt: number; imdb: string; tmdb?: number; }; } interface Props { data: Movie | Show; inCountdown ...

The 'style' property is not found within the 'EventTarget' type

Currently, I am utilizing Vue and TypeScript in an attempt to adjust the style of an element. let changeStyle = (event: MouseEvent) => { if (event.target) { event.target.style.opacity = 1; Although the code is functional, TypeScript consist ...

angular Struggling with @Input and data interpolation functionality within web components

I have built a collection of web components designed for various Angular projects. To make these components reusable, I am using @angular/elements to convert them into custom elements and then serving them via http-server. One of the components I develope ...

Tips on designing unique field validation decorators in NestJS using GraphQL

I'm currently developing a NestJS API using apollo-server-express and have created an InputType for appointments as shown below: @InputType() export class AppointmentInput { @Field(of => String) @IsNotEmpty() name: string; @Field(o ...

Steps for creating a TypeScript class that can implement an interface

As a beginner in TypeScript, I am looking for help with the following code snippet: async function sleep(ms: number) { return new Promise((resolve, reject) => { setTimeout(() => resolve(), ms) }) } async function randomDelay() { ...