Skip to content

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

@benlesh

Description

@benlesh

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions