import { Fn, Transformer } from './type.js' type PipelineArgs = T extends [ Fn, ...any[], ] ? A : never type PipelineOutput = T extends [...any[], Fn] ? O : never type PipelineRecursive< F extends readonly Transformer[], Acc extends any[], > = F extends readonly [Transformer] ? [...Acc, Transformer] : F extends readonly [Transformer, ...infer Tail] ? Tail extends readonly [Transformer, ...any[]] ? PipelineRecursive]> : never : never type Pipeline, ...Transformer[]]> = F extends readonly [Fn] ? [Fn] : F extends readonly [Fn, ...infer Tail] ? Tail extends readonly [Transformer, ...any[]] ? PipelineRecursive]> : never : never /** * This utility function allows to properly type a pipeline of transformers. * * @example * ```ts * // Will be typed as "(input: string) => Promise" * const parse = pipe( * async (input: string) => JSON.parse(input), * async (input: unknown) => { * if (typeof input === 'number') return input * throw new TypeError('Invalid input') * }, * (input: number) => input * 2, * ) * ``` */ export function pipe, ...Transformer[]]>( ...pipeline: Pipeline extends T ? T : Pipeline ) { return pipeline.reduce(pipeTwo) as ( ...args: PipelineArgs ) => Promise> } export function pipeTwo( first: Fn, second: Transformer, ): (...args: A) => Promise { return async (...args) => second(await first(...args)) }