Consider the following TypeScript code:
type operator<T> = (input:T) => T const pipe = <T>(...operators:operator<T>[]) => (input:T):T => operators.reduce((output, f) => f(output), input) const add2:operator<number> = x => x+2 const times3:operator<number> = x => x*3 console.log(pipe(add2, times3)(1)) //output 9
The pipe function simply pipes the input of one operator into the result of the next operator.
Now consider this new definition of the operator type:
type operator<T, U> = (input:T) => U
How should the pipe function be rewritten in order for the IDE to let me know if I am using the types correctly?
E.g.: consider these two operators:
const times3:operator<number, number> = x => x*3 const toStr:operator<number, string> = x => `${x}`
I would like this to work properly:
pipe(times3, toStr)(1)
And here I would like the IDE to warn me that the types are wrong:
pipe(toStr, times3)(1)
I can’t figure this out, thank in advance.
Advertisement
Answer
Here is how RxJS does it:
pipe(): Observable<T>; pipe<A>(op1: OperatorFunction<T, A>): Observable<A>; pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>; pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>; pipe<A, B, C, D>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D> ): Observable<D>; pipe<A, B, C, D, E>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E> ): Observable<E>; pipe<A, B, C, D, E, F>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F> ): Observable<F>; pipe<A, B, C, D, E, F, G>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G> ): Observable<G>; pipe<A, B, C, D, E, F, G, H>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H> ): Observable<H>; pipe<A, B, C, D, E, F, G, H, I>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I> ): Observable<I>; pipe<A, B, C, D, E, F, G, H, I>( op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I>, ...operations: OperatorFunction<any, any>[] ): Observable<unknown>;
It’s not pretty, but it gets the job done.