Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/rxjs/src/combine-latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ type CombineLatestArrayValues<Sources extends readonly ObservableValue<any>[]> =

export const combineLatest: unique symbol = Symbol('combineLatest');

// The empty-tuple constraints preserve array-literal positions without requiring `as const`.
declare global {
interface ObservableCtor {
[combineLatest]: {
<Sources extends readonly ObservableValue<any>[] | { [key: string]: ObservableValue<any> }>(
<Sources extends readonly ObservableValue<any>[] | readonly [] | { [key: string]: ObservableValue<any> }>(
sources: Sources,
config?: { requireAllValues?: boolean }
): Observable<CombineLatestValues<Sources>>;
<Sources extends readonly ObservableValue<any>[], Result>(
<Sources extends readonly ObservableValue<any>[] | readonly [], Result>(
sources: Sources,
project: (...values: CombineLatestArrayValues<Sources>) => Result
): Observable<Result>;
Expand All @@ -28,10 +29,11 @@ declare global {

interface Observable<T> {
[combineLatest]: {
<Sources extends readonly ObservableValue<any>[]>(sources: Sources, config?: { requireAllValues?: boolean }): Observable<
[T, ...CombineLatestArrayValues<Sources>]
>;
<Sources extends readonly ObservableValue<any>[], Result>(
<Sources extends readonly ObservableValue<any>[] | readonly []>(
sources: Sources,
config?: { requireAllValues?: boolean }
): Observable<[T, ...CombineLatestArrayValues<Sources>]>;
<Sources extends readonly ObservableValue<any>[] | readonly [], Result>(
sources: Sources,
project: (value: T, ...values: CombineLatestArrayValues<Sources>) => Result
): Observable<Result>;
Expand Down
46 changes: 46 additions & 0 deletions packages/rxjs/test/types/combine-latest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'rxjs';
import { combineLatest } from 'rxjs/combine-latest';

type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
type Assert<T extends true> = T;

declare const a: Observable<number>;
declare const b: Observable<string>;
declare const receiver: Observable<boolean>;
declare const array: (Observable<number> | Observable<string>)[];
declare const readonlyArray: readonly (Observable<number> | Observable<string>)[];

const tuple = Observable[combineLatest]([a, b]);
type Tuple = Assert<Equal<typeof tuple, Observable<[number, string]>>>;
tuple.subscribe(([n, s]) => {
const x: number = n;
const y: string = s;
});

const configured = Observable[combineLatest]([a, b], { requireAllValues: true });
type Configured = Assert<Equal<typeof configured, Observable<[number, string]>>>;

const readonlyTuple = Observable[combineLatest]([a, b] as const);
type ReadonlyTuple = Assert<Equal<typeof readonlyTuple, Observable<readonly [number, string]>>>;

const arrayResult = Observable[combineLatest](array);
type ArrayResult = Assert<Equal<typeof arrayResult, Observable<(number | string)[]>>>;
const readonlyArrayResult = Observable[combineLatest](readonlyArray);
type ReadonlyArrayResult = Assert<Equal<typeof readonlyArrayResult, Observable<readonly (number | string)[]>>>;

const object = Observable[combineLatest]({ count: a, label: b });
type ObjectResult = Assert<Equal<typeof object, Observable<{ count: number; label: string }>>>;

const empty = Observable[combineLatest]([]);
type Empty = Assert<Equal<typeof empty, Observable<[]>>>;

const projected = Observable[combineLatest]([a, b], (n, s) => n.toFixed() + s.toUpperCase());
type Projected = Assert<Equal<typeof projected, Observable<string>>>;

const instance = receiver[combineLatest]([a, b]);
type Instance = Assert<Equal<typeof instance, Observable<[boolean, number, string]>>>;
const instanceProjected = receiver[combineLatest]([a, b], (enabled, n, s) => (enabled ? n.toFixed() + s.toUpperCase() : ''));
type InstanceProjected = Assert<Equal<typeof instanceProjected, Observable<string>>>;

// @ts-expect-error Each source must be an ObservableValue.
Observable[combineLatest]([a, 42]);