Skip to content

Commit

Permalink
feat: add ObservedValueUnion/TupleFromArray (#5254)
Browse files Browse the repository at this point in the history
* feat: add observed value union and tuple types

* chore: rename to use observed value union type

* chore: tweak test to make dtslint deterministic

For some reason, the number[] type shows up in seemingly random places
in the union.
  • Loading branch information
cartant authored Jan 23, 2020
1 parent 1e7d469 commit 338b174
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 12 deletions.
2 changes: 1 addition & 1 deletion spec-dtslint/observables/of-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ it('should support mixed type of 9 params', () => {
});

it('should support mixed type of 13 params', () => {
const res = of(a, b, c, d, e, f, g, h, i, j, '', true, 123, [1, 2, 3]); // $ExpectType Observable<string | number | boolean | A | B | C | D | E | F | G | number[] | H | I | J>
const res = of(a, b, c, d, e, f, g, h, i, j, '', true, 123, 10n); // $ExpectType Observable<string | number | bigint | boolean | A | B | C | D | E | F | G | H | I | J>
});

it('should support a rest of params', () => {
Expand Down
67 changes: 67 additions & 0 deletions spec-dtslint/types-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
Observable,
ObservedValueOf,
ObservedValueUnionFromArray,
ObservedValueTupleFromArray,
Unshift
} from 'rxjs';
import { A, B, C } from './helpers';

describe('ObservedValueOf', () => {
it('should infer from an observable', () => {
let explicit: ObservedValueOf<Observable<A>>;
let inferred = explicit!; // $ExpectType A
});

it('should infer from an array', () => {
let explicit: ObservedValueOf<A[]>;
let inferred = explicit!; // $ExpectType A
});

it('should infer from a promise', () => {
let explicit: ObservedValueOf<Promise<A>>;
let inferred = explicit!; // $ExpectType A
});
});

describe('ObservedUnionFromArray', () => {
it('should infer from an array of observables', () => {
let explicit: ObservedValueUnionFromArray<[Observable<A>, Observable<B>]>;
let inferred = explicit!; // $ExpectType A | B
});

it('should infer from an array of arrays', () => {
let explicit: ObservedValueUnionFromArray<[A[], B[]]>;
let inferred = explicit!; // $ExpectType A | B
});

it('should infer from an array of promises', () => {
let explicit: ObservedValueUnionFromArray<[Promise<A>, Promise<B>]>;
let inferred = explicit!; // $ExpectType A | B
});
});

describe('ObservedTupleFromArray', () => {
it('should infer from an array of observables', () => {
let explicit: ObservedValueTupleFromArray<[Observable<A>, Observable<B>]>;
let inferred = explicit!; // $ExpectType [A, B]
});

it('should infer from an array of arrays', () => {
let explicit: ObservedValueTupleFromArray<[A[], B[]]>;
let inferred = explicit!; // $ExpectType [A, B]
});

it('should infer from an array of promises', () => {
let explicit: ObservedValueTupleFromArray<[Promise<A>, Promise<B>]>;
let inferred = explicit!; // $ExpectType [A, B]
});
});

describe('Unshift', () => {
it('should add the type to the beginning of the tuple', () => {
let tuple: ObservedValueTupleFromArray<[Observable<A>, Observable<B>]>;
let explicit: Unshift<typeof tuple, C>;
let inferred = explicit!; // $ExpectType [C, A, B]
});
});
4 changes: 2 additions & 2 deletions src/internal/observable/concat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Observable } from '../Observable';
import { ObservableInput, SchedulerLike, ObservedValueOf, ObservedValuesFromArray } from '../types';
import { ObservableInput, SchedulerLike, ObservedValueOf, ObservedValueUnionFromArray } from '../types';
import { of } from './of';
import { concatAll } from '../operators/concatAll';

Expand All @@ -17,7 +17,7 @@ export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInp
/** @deprecated remove in v8. Passing a scheduler to concat is deprecated, please use {@link scheduled} and {@link concatAll} `scheduled([o1, o2], scheduler).pipe(concatAll())` */
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>, O5 extends ObservableInput<any>, O6 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4> | ObservedValueOf<O5> | ObservedValueOf<O6>>;

export function concat<A extends ObservableInput<any>[]>(...observables: A): Observable<ObservedValuesFromArray<A>>;
export function concat<A extends ObservableInput<any>[]>(...observables: A): Observable<ObservedValueUnionFromArray<A>>;

/* tslint:enable:max-line-length */
/**
Expand Down
4 changes: 2 additions & 2 deletions src/internal/observable/forkJoin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Observable } from '../Observable';
import { ObservableInput, ObservedValuesFromArray, ObservedValueOf, SubscribableOrPromise } from '../types';
import { ObservableInput, ObservedValueUnionFromArray, ObservedValueOf, SubscribableOrPromise } from '../types';
import { isArray } from '../util/isArray';
import { map } from '../operators/map';
import { isObject } from '../util/isObject';
Expand Down Expand Up @@ -30,7 +30,7 @@ export function forkJoin<A, B, C>(sources: [ObservableInput<A>, ObservableInput<
export function forkJoin<A, B, C, D>(sources: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>, ObservableInput<D>]): Observable<[A, B, C, D]>;
export function forkJoin<A, B, C, D, E>(sources: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>, ObservableInput<D>, ObservableInput<E>]): Observable<[A, B, C, D, E]>;
export function forkJoin<A, B, C, D, E, F>(sources: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>, ObservableInput<D>, ObservableInput<E>, ObservableInput<F>]): Observable<[A, B, C, D, E, F]>;
export function forkJoin<A extends ObservableInput<any>[]>(sources: A): Observable<ObservedValuesFromArray<A>[]>;
export function forkJoin<A extends ObservableInput<any>[]>(sources: A): Observable<ObservedValueUnionFromArray<A>[]>;

// forkJoin({})
export function forkJoin(sourcesObject: {}): Observable<never>;
Expand Down
8 changes: 4 additions & 4 deletions src/internal/operators/concatWith.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { concat as concatStatic } from '../observable/concat';
import { Observable } from '../Observable';
import { ObservableInput, OperatorFunction, ObservedValuesFromArray } from '../types';
import { ObservableInput, OperatorFunction, ObservedValueUnionFromArray } from '../types';

export function concatWith<T>(): OperatorFunction<T, T>;
export function concatWith<T, A extends ObservableInput<any>[]>(...otherSources: A): OperatorFunction<T, ObservedValuesFromArray<A> | T>;
export function concatWith<T, A extends ObservableInput<any>[]>(...otherSources: A): OperatorFunction<T, ObservedValueUnionFromArray<A> | T>;

/**
* Emits all of the values from the source observable, then, once it completes, subscribes
Expand Down Expand Up @@ -43,9 +43,9 @@ export function concatWith<T, A extends ObservableInput<any>[]>(...otherSources:
*
* @param otherSources Other observable sources to subscribe to, in sequence, after the original source is complete.
*/
export function concatWith<T, A extends ObservableInput<any>[]>(...otherSources: A): OperatorFunction<T, ObservedValuesFromArray<A> | T> {
export function concatWith<T, A extends ObservableInput<any>[]>(...otherSources: A): OperatorFunction<T, ObservedValueUnionFromArray<A> | T> {
return (source: Observable<T>) => source.lift.call(
concatStatic(source, ...otherSources),
undefined
) as Observable<ObservedValuesFromArray<A> | T>;
) as Observable<ObservedValueUnionFromArray<A> | T>;
}
34 changes: 31 additions & 3 deletions src/internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,40 @@ export interface SchedulerAction<T> extends Subscription {
export type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;

/**
* Extracts the generic type from an `ObservableInput<any>[]`.
* Extracts a union of element types from an `ObservableInput<any>[]`.
* If you have `O extends ObservableInput<any>[]` and you pass in
* `Observable<string>[]` or `Promise<string>[]` you would get
* back a type of `string`
* back a type of `string`.
* If you pass in `[Observable<string>, Observable<number>]` you would
* get back a type of `string | number`.
*/
export type ObservedValuesFromArray<X> = X extends Array<ObservableInput<infer T>> ? T : never;
export type ObservedValueUnionFromArray<X> =
X extends Array<ObservableInput<infer T>>
? T
: never;

/** @deprecated use {@link ObservedValueUnionFromArray} */
export type ObservedValuesFromArray<X> = ObservedValueUnionFromArray<X>;

/**
* Extracts a tuple of element types from an `ObservableInput<any>[]`.
* If you have `O extends ObservableInput<any>[]` and you pass in
* `[Observable<string>, Observable<number>]` you would get back a type
* of `[string, number]`.
*/
export type ObservedValueTupleFromArray<X> =
X extends Array<ObservableInput<any>>
? { [K in keyof X]: ObservedValueOf<X[K]> }
: never;

/**
* Adds a type to the beginning of a tuple.
* If you pass in `Unshift<[B, C], A>` you will get back `[A, B, C]`.
*/
export type Unshift<X extends any[], Y> =
((arg: Y, ...rest: X) => any) extends ((...args: infer U) => any)
? U
: never;

/**
* Extracts the generic value from an Array type.
Expand Down

0 comments on commit 338b174

Please sign in to comment.