Reputation: 2070
I need to program a value transformation based on various steps. In order to reuse the steps, an architecture was proposed where the steps can be passed to the transformation, similar to this code (e.g. to uppercase a long string and truncate it after 13 characters):
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) + '...';
}
}
Is there a way to make sure (preferrably at compile time) that the steps are compatible with each other, i.e. that the output of one step is type-compatible with the input of the next, so the following class isn't allowed after a step producing a string:
class SquareRoot {
public run(num: number): number {
return Math.sqrt(num);
}
}
Achieving this at runtime seems possible by adding the two members inputType
and outputType
to the steps and comparing them, but I'd like to do the check at compile time.
Upvotes: 1
Views: 34
Reputation: 25820
First, let's define the Transformer
class.
class Transformation<T> {
constructor(readonly value: T) {
this.value = value;
}
public static from<U>(input: U): Transformation<U> {
return new Transformation(input);
}
public step<U>(transformer: Transformer<T, U>): Transformation<U> {
return new Transformation(transformer.run(this.value));
}
}
It uses a Transformer
interface. We can define it as:
interface Transformer<T, U> {
run(input: T): U;
}
With that in place, the transformations are guaranteed to be correct in compile-time.
Transformation.from("This is a long story")
.step(new Uppercase())
.step(new Truncate()); // OK
Transformation.from("This is a long story")
.step(new Uppercase())
.step(new SquareRoot()); // Compile-time error: Type 'string' is not assignable to type 'number'.ts(2345)
Update
Note: in your example, you're passing constructors of Uppercase
and Truncate
as your step
s. In my solution, instances are used instead. If you insist on passing constructors instead, you will need to make the run
method static.
class Uppercase {
public static run(str: string): string {
return str.toUpperCase();
}
}
class Truncate {
public static run(str: string): string {
return str.substr(0, 10) + '...';
}
}
class SquareRoot {
public static run(num: number): number {
return Math.sqrt(num);
}
}
Transformation.from("This is a long story")
.step(Uppercase)
.step(Truncate);
Transformation.from("This is a long story")
.step(Uppercase)
.step(SquareRoot); // Compile-time error: Type 'string' is not assignable to type 'number'.ts(2345)
Upvotes: 2