The problem I'm trying to solve, is we have a TON of overloads for functions like forkJoin in RxJS. Which can be seen here.
Basically this pattern were you have to have N overloads for N arguments:
export function forkJoin<A>(sources: [ObservableInput<A>]): Observable<[A]>;
export function forkJoin<A, B>(sources: [ObservableInput<A>, ObservableInput<B>]): Observable<[A, B]>;
export function forkJoin<A, B, C>(sources: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>]): Observable<[A, B, C]>;
What we really would like to be able to do is figure out a way to type this in just a few lines... and I was able to get very very close today, but it's still not quite right.
TypeScript Version: 4.1.0-dev.20201013
Search Terms:
Code
class Observable<T> {
observable!: T;
}
class ObservableInput<T> {
observableInput!: T
}
function forkJoin<O extends readonly ObservableInput<any>[]>(args: O): Observable<ObservableMapper<O>>;
function forkJoin<O extends readonly ObservableInput<any>[]>(...args: O): Observable<ObservableMapper<O>>;
function forkJoin(args: any): Observable<ObservableMapper<any>> {
return {} as any;
}
declare let os: [ ObservableInput<1>, ObservableInput<2>, ObservableInput<3>, ObservableInput<4> ];
declare let o1: ObservableInput<number>
declare let o2: ObservableInput<string>;
const os2 = [o1, o2] as const;
const foo = forkJoin(o1, o2); // $ExpectType Observable<[number, string]>
const bar = forkJoin([o1, o2] as const); // $ExpectType Observable<[number, string]>
const bar2 = forkJoin([o1, o2]); // $ExpectType Observable<[number, string]>
const baz = forkJoin(os); // $ExpectType Observable<[1, 2, 3, 4]>
const baz2 = forkJoin(os2); // $ExpectType Observable<[number, string]>
type ObservableMapper<T extends readonly any[]> =
T extends [] ? [] :
T extends readonly [ObservableInput<infer U>, ...infer Rest] ? [U, ...ObservableMapper<Rest>] :
T extends ObservableInput<infer U>[] ? U[] : never;
Expected behavior:
forkJoin([o1, o2]) (aka bar2) would be of type Observable<[number, string]>.
Actual behavior:
forkJoin([o1, o2]) (aka bar2) is of type Observable<(number | string)[]>.
Playground Link:
https://www.typescriptlang.org/play?ts=4.1.0-dev.20201013#code/MYGwhgzhAEDyBGECmAnAbmeIkB4AqAfNAN4BQ0F0A9oqhlkgIQBc0eA3KQL6mmiQwEydJmwBJAHYAHAK4AXfETKVqtEQ0my5LNt14AzGROByAllQnR9VFAGsAUlVMScsaEgAecpBIAmgtXpxaXkcMAkATwIAbQBdAgAKADoUsBQAcwhWWABKbMDRXCE6QoBZMCkpVFcCAk5SQ2MzCysbBycXN09vPwDhIKRNUPCouMS0zOy8uAKGV1nscsrq2Fr6xpNzS2s7R2cEiazoEeni9Wx5-rKKqpQwyNqSckoUJDkZFEtiLmOYEc4eKRfEh+K9oNg5NQjtEZlcNCEFABGAgAGlhJXhWhwACZUejzoMETgAMx4s4DIYKAAsRFi9WBoKQ4Le1ER+ThwSxEhkAFt4KgCECQeAwRDqNj2RjOaEIHIUM50nU+BZZa0qNAALytXYdBJURFoqjYnLKiSq+BpTXa9r7aL6w3Y2I5epyCJVfEDJa3fDuLw+fzQV5gXwWEARY6RMaa54UPC+noBuLQAD80CTzBjbHj-pg0XJhUpOGc+lQ0AAqniUkli6WAEpIWWxFNpstoqv5hhe6r12UEJusTNx7o5j0Fok1lDlmJN1Nl9PQCRINCoThAA
cc @kolodny @cartant @RyanCavanaugh @DanielRosenwasser
FWIW, I also tried a technique with [Index in keyof O] that got me pretty close pre-4.1 playground here
class Observable<T> {
observable!: T;
}
class ObservableInput<T> {
observableInput!: T
}
function forkJoin<O extends readonly unknown[]>(...args: [...ObservablesArrayThing<O>]): Observable<O>;
function forkJoin<O extends readonly unknown[]>(args: [...ObservablesArrayThing<O>]): Observable<O>;
function forkJoin(...args: any[]): any {
return {} as any;
}
declare let os: [ ObservableInput<1>, ObservableInput<2>, ObservableInput<3>, ObservableInput<4> ];
declare let o1: ObservableInput<number>
declare let o2: ObservableInput<string>;
const foo = forkJoin(o1, o2)
const bar = forkJoin([o1, o2]);
type ObservablesArrayThing<T> = {
[K in keyof T]: ObservableInput<T[K]>
}
That solution works in TypeScript 4.0 by the way.
Okay, but I'm calling it ObservablesArrayThing.
Be sure to leave a big comment that says "naming things is hard, if you don't like it take it up with @DanielRosenwasser" 馃槄
OMFG we had something so close: https://github.com/ReactiveX/rxjs/blob/c0514c11bdcbb46770fcb1344737fa8710cf0b0f/src/internal/types.ts#L209
I've changed the title so people aren't confused when searching issues, @DanielRosenwasser
Most helpful comment
I've changed the title so people aren't confused when searching issues, @DanielRosenwasser