Skip to content

Instantly share code, notes, and snippets.

@stevenwaterman
Last active August 20, 2020 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevenwaterman/883d100e647cabfa7d9d9dfad29bb657 to your computer and use it in GitHub Desktop.
Save stevenwaterman/883d100e647cabfa7d9d9dfad29bb657 to your computer and use it in GitHub Desktop.
Typescript 4 is a magical thing
type Tail<XS extends readonly any[]> = XS extends readonly [any, ...infer T] ? T : [];
type Last<XS extends readonly any[]> = XS extends readonly [...infer _, infer X] ? X : never;
type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;
type Equal<A, B> = A extends B ? B extends A ? true : false : false;
type PipelineStage<Input, Config extends Record<any, unknown>, Output> = (input: Input, config: Config) => Output;
type Input<Stage extends PipelineStage<any, any, any>> = Parameters<Stage>[0];
type Config<Stage extends PipelineStage<any, any, any>> = Parameters<Stage>[1];
type Output<Stage extends PipelineStage<any, any, any>> = ReturnType<Stage>;
type PipelineInput<Stages extends readonly [PipelineStage<any, any, any>, ...any[]]> = Input<Stages[0]>;
type PipelineConfig<Stages extends readonly PipelineStage<any, any, any>[]> = UnionToIntersection<Config<Stages[number]>>;
type PipelineOutput<Stages extends readonly PipelineStage<any, any, any>[]> = Output<Last<Stages>>;
type Match<First extends PipelineStage<any, any, any>, Second extends PipelineStage<any, any, any>> = Equal<Output<First>, Input<Second>>;
type ValidPipeline<Stages extends readonly PipelineStage<any, any, any>[]> =
Stages extends readonly [any] ? true
: Stages extends readonly [any, any, ...any[]] ? Match<Stages[0], Stages[1]> extends true ? ValidPipeline<Tail<Stages>>
: false : false;
type Pipeline<Stages extends readonly PipelineStage<any, any, any>[]> = ValidPipeline<Stages> extends true ? Stages : never;
function createPipeline<Stages extends readonly [PipelineStage<any, any, any>, ...PipelineStage<any, any, any>[]]>(pipeline: Pipeline<Stages>): (input: PipelineInput<Stages>, config: PipelineConfig<Stages>) => PipelineOutput<Stages> {
return (input: PipelineInput<Stages>, config: PipelineConfig<Stages>) => {
let state: any = input;
for(const stage of pipeline) {
state = stage(state, config)
}
return state;
}
}
function reverse(input: string, config: {}) {
return input.split("").reverse().join("");
}
function toInt(input: string, config: {radix: number}) {
return parseInt(input, config.radix);
}
function multiply(input: number, config: {multiplicand: number}) {
return input * config.multiplicand;
}
const pipeline = createPipeline([reverse, toInt, multiply])
const output = pipeline("524", {radix: 10, multiplicand: 5})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment