Signature of the method relies on the method call made earlier

I am currently tasked with implementing a value transformation process that involves multiple steps. To ensure reusability of these steps, a proposed architecture allows for passing steps to the transformation function. For example, transforming a long string to uppercase and then truncating it:

Transformation.from("This is a long story").step(Uppercase).step(Truncate);

class Uppercase {
  public run(str: string): string {
    return str.toUpperCase();
  }
}

class Truncate {
  public run(str: string): string {
    return str.substr(0, 10) + '...';
  }
}

I am interested in finding a way (ideally at compile time) to ensure that the steps are compatible with each other. This means verifying that the output of one step is type-compatible with the input of the next step, preventing scenarios like the following where there is a mismatch in types:

class SquareRoot {
  public run(num: number): number {
    return Math.sqrt(num);
  }
}

While this verification can be done at runtime by comparing input and output types, I am seeking a method to perform this check at compile time.

Answer №1

To start, we need to establish the concept of the Morpher class.

class Morph<T> {
  constructor(readonly value: T) {
    this.value = value;
  }

  public static create<U>(input: U): Morph<U> {
    return new Morph(input);
  }

  public evolve<U>(morpher: Morpher<T, U>): Morph<U> {
    return new Morph(morpher.execute(this.value));
  }
}

This relies on a Morpher interface. It can be defined as follows:

interface Morpher<T, U> {
  execute(input: T): U;
}

By implementing this structure, the transformations are ensured to be accurate at compile-time.

Morph.create("Once upon a time")
  .evolve(new Capitalize())
  .evolve(new Shorten()); // Works fine

Morph.create("Once upon a time")
  .evolve(new Capitalize())
  .evolve(new Multiplication()); // Compilation error: Type 'string' cannot be assigned to type 'number'.ts(2345)

Update

Note: In your instance, you are passing the constructors of Capitalize and Shorten as your evolve steps. In my solution, I am using instances instead. If you prefer to use constructors, you must make the execute method static.

class Capitalize {
  public static execute(str: string): string {
    return str.toUpperCase();
  }
}

class Shorten {
  public static execute(str: string): string {
    return str.substr(0, 12) + '...';
  }
}

class Multiplication {
  public static execute(num: number): number {
    return num * num;
  }
}

Morph.create("Once upon a time")
  .evolve(Capitalize)
  .evolve(Shorten);

Morph.create("Once upon a time")
  .evolve(Capitalize)
  .evolve(Multiplication); // Compilation error: Type 'string' cannot be assigned to type 'number'.ts(2345)

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

When using Angular 4 CanActivate guard in conjunction with a service, the component may not load properly. However, by simply using Observable

When attempting to implement the CanActivate guard in Angular, I encountered an issue where it didn't work when calling a service and using return true, or even return Observable.of(true);. However, the guard did work and the component loaded success ...

Testing the Compatibility of Angular JS and Angular 8 in a Hybrid Application

I am currently working on a hybrid application using AngularJS and Angular 8. The new components I create in Angular need to be downgraded for use in AngularJS. Here is a snippet of the module code: @NgModule({ // Declarations and Imports providers ...

What are the optimal strategies for managing various components within an Angular (2) Web application?

I am seeking advice on Angular 2+ Webapps and have a few questions. What is the recommended approach for managing a publicly available info page, an authentication page, and a protected page for signed-in users? Should I consider separate Angular Apps ...

Encountered an issue during the transition from Angular 7 to Angular 9

After following the advice in the second response of this discussion, I successfully upgraded to Angular 9. However, I am now encountering an issue in the browser console when running my project. https://i.sstatic.net/esAXf.png Package.json "dependencie ...

Angularfire2 retrieve list of data with a specified number of items from the

I am facing a challenge in retrieving a specific node from my firebase database. The technologies I am using include: "angularfire2": "^5.0.0-rc.4", "firebase": "^4.9.0", In my component code, you can find the following lines : this.id = this.route. ...

The parameter cannot be assigned to type 'HTMLCanvasElement | null' due to conflicting arguments

I am encountering an issue with the following code, as it fails to compile: import React, {useEffect} from 'react' import {Card, Image} from 'semantic-ui-react' import * as chart from 'chart.js' export const PieChartCard = ...

The parameter cannot be assigned a value of type 'string' as it requires a value that matches the format '`${string}` | `${string}.${string}` | `${string}.${number}`'

I recently updated my react-hook-forms from version 6 to version 7. Following the instructions in the migration guide, I made changes to the register method. However, after making these changes, I encountered the following error: The argument being pass ...

Tips for fixing TypeScript compiler error TS2339: Issue with accessing 'errorValue' property in Angular 5 project

Within a component, I have developed a function to manage errors returned from a Rest Service and determine the corresponding error message to display to the user. This method accepts an error object (custom data structure from the service), navigates to e ...

What is the method for verifying that one type extends another in the TypeScript compiler API?

In the process of building a tool (partly to test its functionality), I am developing a way to condense a set of TypeScript definitions into a clean d.ts file, while ignoring unnecessary helper types used for reshaping data. This approach is proving quite ...

I am looking for a way to convert the date format from "yyyy-MM-dd" to "dd-MM-yyyy" in NestJs

I need help with changing the date format from "yyyy-MM-dd" to "dd-MM-yyyy". Currently, my entity code looks like this: @IsOptional() @ApiProperty({ example: '1999-12-12', nullable: true }) @Column({ type: 'date', nullable: true } ...

Creating IPv6 Mask from IPv6 Prefix Using Javascript: A Step-by-Step Guide

Write a JavaScript/TypeScript function that can convert IPv6 prefixes (ranging from 0 to 128) into the corresponding mask format (using the ffff:ffff style). Here are some examples: 33 => 'ffff:ffff:8000:0000:0000:0000:0000:0000' 128 => ...

When utilizing an object as state in React, the `setState` function will only set

Currently, I am working on developing a React form that utilizes a custom Input component which I have created multiple times. The objective is to gather all the names and values from the form in the parent component: {inputName: value, inputName2: valu ...

The Console.Log function will not run if it is placed within the RXJS Tap operator

In my current setup, I have the following observables: this.authenticationService.isSignedIn() -> Observable<Boolean> this.user$ -> Observable<UserModel> I am in need of checking a condition based on both these observables, so I attempt ...

The Typescript "and" operator is used for combining multiple conditions

I'm having difficulty understanding the functionality of the & operator in TypeScript. I recently encountered this code snippet: type IRecord<T> = T & TypedMap<T>; Can someone explain what this operator does and how it differs fr ...

Packaging a NodeJS project in Visual Studio - A step-by-step guide to creating and setting up an N

In my VS2013 solution, I have a combination of NodeJS (using TypeScript) and C# class library projects connected by EdgeJS. Among the NodeJS projects, one serves as a library for a RabbitMQ bus implementation, while two are applications meant to be hosted ...

Caution in version 3.5.1 of Vue Router: The tag prop of `<router-link>` is now obsolete and has been eliminated in Vue Router 4

After updating the node packages of our Vue.js app, a warning is now appearing in the browser console: [vue-router] The 'tag' prop has been deprecated and removed in Vue Router 4. To remove this warning, use the v-slot API. Check out the migrat ...

Tips for updating the value.replace function for the "oninput" attribute within Angular 7

I need to modify an input based on a value from a TypeScript variable in the oninput attribute. This modification should only apply to English characters. In my HTML file: <input class="form-control" oninput="value=value.replace(r ...

Encountered a problem with AngularUniversal prerendering: UnhandledPromiseRejectionWarning: Unable to locate NgModule metadata for 'class{}'

Objective The task may seem lengthy, but it's straightforward! Currently, I am utilizing Angular Universal for Server-Side Rendering (SSR) by following a tutorial. The Universal/express-engine has been installed, main.js is generated in the dist/pro ...

Eliminate the need for require statements in TypeScript-generated JavaScript files

I am working on a website project and utilizing TypeScript for development. While using the tsc compiler, I noticed that all my JavaScript code compiles correctly. However, when I include an import statement in my TypeScript files, it gets compiled into J ...

Error: A stream was expected, but you provided 'undefined'. Please provide either an Observable, Promise, Array, or Iterable instead

I'm encountering an error while trying to catch errors in my Ionic-based application with Angular. In the create() method, I am attempting to create a new User. If the username already exists, I receive a response from the backend, but my method thro ...