Typescript: TS 4.0 was totally fine for what I wanted to do and 4.1 is, too. Daniel R. is awesome.

Created on 14 Oct 2020  路  7Comments  路  Source: microsoft/TypeScript

This could totally by my usage!!

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

Question

Most helpful comment

I've changed the title so people aren't confused when searching issues, @DanielRosenwasser

All 7 comments

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" 馃槄

I've changed the title so people aren't confused when searching issues, @DanielRosenwasser

Was this page helpful?
0 / 5 - 0 ratings