When using tuple types in generic functions, how can you 'extend' the source type?
Let's say we want to make a RxJS mapping operator that returns the source observable value(s) together with another mapped value:
export function mapExtended<T, R>(mapping: (input: [T]) => R): OperatorFunction<[T], [T, R]>;
export function mapExtended<T1, T2, R>(mapping: (input: [T1, T2]) => R): OperatorFunction<[T1, T2], [T1, T2, R]>;
export function mapExtended<T1, T2, T3, R>(mapping: (input: [T1, T2, T3]) => R): OperatorFunction<[T1, T2, T3], [T1, T2, T3, R]>;
export function mapExtended<R>(mapping: (input: any) => R): OperatorFunction<any, {}> {
return (source$: Observable<any>) => source$.pipe(
map(input => {
const mappingResult = mapping(input);
return [...input, mappingResult];
}),
);
}
That seems to work but the overloads aren't detected properly. The return type for test
is, Observable<[[number, number], number]>
instead of the expected Observable<[number, number, number]>
:
const test = combineLatest(of(1), of(2)).pipe(
mapExtend(([s1, s2]) => s1 + s2),
);
Is there some sort of conditional type checking to say T
cannot be a tuple type?
I tried accomplishing the same with mapped type support for tuples, but to no avail, as I cannot seem to (or I do not know how to) 'extend' the mapped type:
type MapExtended2Input<T> = { [P in keyof T]: T[P] };
function mapExtended2<T extends any[], R>(mapping: (input: MapExtended2Input<T>) => R): OperatorFunction<MapExtended2Input<T>, [MapExtended2Input<T>, R]> {
return (source$: Observable<MapExtended2Input<T>>) => source$.pipe(
map(input => {
const mappingResult = mapping(input);
const result: [MapExtended2Input<T>, R] = [input, mappingResult];
return result;
}),
);
}
const test2 = combineLatest(of(1), of(2)).pipe(
mapExtended2(([s1, s2]) => s1 + s2),
);
Here, the return type is also Observable<[[number, number], number]>
, which is expected, but I do not know how to 'add' a type to the mapped tuple type. Intersecting doesn't seem to work or I'm doing it wrong.
An example of the desired functionality without RxJS would be:
Let's say I need a function myFunc
thats has 2 generic type parameters:
- a tuple type T, with variable number of elements
- another type R
The result of the function should need to be a tuple type containing all elements of the tuple type T with the parameter of type R appended to it.
e.g.:
myFunc<T, R>(x, y); // Should return [T, R]
myFunc<[T1, T2], R>(x, y); // Should return [T1, T2, R]
myFunc<[T1, T2, T3], R>; // Should return [T1, T2, T3, R]
// ...