Evite explosão de sobrecarga de função
Aug 22 2020
O TypeScript fornece uma maneira de evitar esse tipo de explosão de sobrecarga de método e, ao fazer isso, fornece segurança de tipo para um número ilimitado de varargs?
type Operator<FROM, TO> = (source: Stream<FROM>) => Stream<TO>
class Stream<V> {
// ...
pipe<A>(operator: Operator<VALUE, A>): Stream<A>
pipe<A, B>(op1: Operator<VALUE, A>, op2: Operator<A, B>): Stream<B>
pipe<A, B, C>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>): Stream<C>
pipe<A, B, C, D>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>): Stream<D>
pipe<A, B, C, D, E>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>): Stream<E>
pipe<A, B, C, D, E, F>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>): Stream<F>
pipe<A, B, C, D, E, F, G>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>): Stream<G>
pipe<A, B, C, D, E, F, G, H>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>): Stream<H>
pipe<A, B, C, D, E, F, G, H, I>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>): Stream<I>
pipe<A, B, C, D, E, F, G, H, I, J>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>): Stream<J>
pipe<A, B, C, D, E, F, G, H, I, J, K>(op1: Operator<VALUE, A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>, op6: Operator<E, F>, op7: Operator<F, G>, op8: Operator<G, H>, op9: Operator<H, I>, op10: Operator<I, J>, ...restOps: Operator<unknown, unknown>[]): Stream<K>
pipe<TO_VALUE>(operator: Operator<VALUE, unknown>, ...restOperators: Operator<unknown, unknown>[]): Stream<TO_VALUE> {
return restOperators.reduce((stream, operator) => operator(stream), this as Stream<unknown>) as Stream<TO_VALUE>
}
}
Observe que cada Operatortipo de saída é o próximo Operatortipo de entrada.
Respostas
3 MingweiSamuel Sep 08 2020 at 07:31
É possível.
Aqui está um exemplo funcional que requer tipos de tupla TypeScript 4. A ideia é usar uma matriz genérica Tpara rastrear cada tipo consecutivo para garantir que os operadores concordem.
Eu baseei essa resposta neste comentário de @jcalz.
type Operator<FROM, TO> = (source: Stream<FROM>) => Stream<TO>
type Prev<T extends any[], K, D> = K extends keyof [ D, ...T ] ? [ D, ...T ][K] : never;
type Operators<VALUE, T extends any[]> = {
[K in keyof T]: Operator<Prev<T, K, VALUE>, T[K]>
};
type PipeResult<T extends any[]> = T extends [ ...infer _, infer U ] ? Stream<U> : never;
class Stream<VALUE> {
x?: VALUE; // Needed to make sure Stream<X> doesn't always extend Stream<Y>.
pipe<T extends any[]>(...operators: Operators<VALUE, T>): PipeResult<T> {
return operators.reduce<Stream<unknown>>((stream, operator) => operator(stream), this) as PipeResult<T>;
}
}
Teste:
const OpA: Operator<number, 'hi'> = null as any;
const OpB: Operator<string, 'hello'> = null as any;
const OpC: Operator<string, Date> = null as any;
const OpD: Operator<1521, string> = null as any;
const OpE: Operator<5, 6> = null as any;
const OpF: Operator<6, 7> = null as any;
const NumberStream = new Stream<number>();
// x0: Stream<Date>
const x0 = NumberStream.pipe(OpA, OpB, OpC);
// x1: Stream<"hello">
const x1 = NumberStream.pipe(
OpA, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB,
OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB,
OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB);
// Error on arg 3.
const x2 = NumberStream.pipe(OpA, OpC, OpC);
// Error on arg 1.
const x3 = NumberStream.pipe(OpD, OpE, OpF);
Link Playground
Antiga resposta ruim: Link do Playground
O que significa um erro “Não é possível encontrar o símbolo” ou “Não é possível resolver o símbolo”?
Christopher Nolan uma vez se arrependeu de ter lido o 'roteiro de Pulp Fiction' de Quentin Tarantino