diff --git a/packages/rxjs/src/combine-latest.ts b/packages/rxjs/src/combine-latest.ts index 31494372a3..f36106e08b 100644 --- a/packages/rxjs/src/combine-latest.ts +++ b/packages/rxjs/src/combine-latest.ts @@ -12,14 +12,15 @@ type CombineLatestArrayValues[]> = export const combineLatest: unique symbol = Symbol('combineLatest'); +// The empty-tuple constraints preserve array-literal positions without requiring `as const`. declare global { interface ObservableCtor { [combineLatest]: { - [] | { [key: string]: ObservableValue }>( + [] | readonly [] | { [key: string]: ObservableValue }>( sources: Sources, config?: { requireAllValues?: boolean } ): Observable>; - [], Result>( + [] | readonly [], Result>( sources: Sources, project: (...values: CombineLatestArrayValues) => Result ): Observable; @@ -28,10 +29,11 @@ declare global { interface Observable { [combineLatest]: { - []>(sources: Sources, config?: { requireAllValues?: boolean }): Observable< - [T, ...CombineLatestArrayValues] - >; - [], Result>( + [] | readonly []>( + sources: Sources, + config?: { requireAllValues?: boolean } + ): Observable<[T, ...CombineLatestArrayValues]>; + [] | readonly [], Result>( sources: Sources, project: (value: T, ...values: CombineLatestArrayValues) => Result ): Observable; diff --git a/packages/rxjs/test/types/combine-latest.ts b/packages/rxjs/test/types/combine-latest.ts new file mode 100644 index 0000000000..620a656d7e --- /dev/null +++ b/packages/rxjs/test/types/combine-latest.ts @@ -0,0 +1,46 @@ +import 'rxjs'; +import { combineLatest } from 'rxjs/combine-latest'; + +type Equal = (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2 ? true : false; +type Assert = T; + +declare const a: Observable; +declare const b: Observable; +declare const receiver: Observable; +declare const array: (Observable | Observable)[]; +declare const readonlyArray: readonly (Observable | Observable)[]; + +const tuple = Observable[combineLatest]([a, b]); +type Tuple = Assert>>; +tuple.subscribe(([n, s]) => { + const x: number = n; + const y: string = s; +}); + +const configured = Observable[combineLatest]([a, b], { requireAllValues: true }); +type Configured = Assert>>; + +const readonlyTuple = Observable[combineLatest]([a, b] as const); +type ReadonlyTuple = Assert>>; + +const arrayResult = Observable[combineLatest](array); +type ArrayResult = Assert>>; +const readonlyArrayResult = Observable[combineLatest](readonlyArray); +type ReadonlyArrayResult = Assert>>; + +const object = Observable[combineLatest]({ count: a, label: b }); +type ObjectResult = Assert>>; + +const empty = Observable[combineLatest]([]); +type Empty = Assert>>; + +const projected = Observable[combineLatest]([a, b], (n, s) => n.toFixed() + s.toUpperCase()); +type Projected = Assert>>; + +const instance = receiver[combineLatest]([a, b]); +type Instance = Assert>>; +const instanceProjected = receiver[combineLatest]([a, b], (enabled, n, s) => (enabled ? n.toFixed() + s.toUpperCase() : '')); +type InstanceProjected = Assert>>; + +// @ts-expect-error Each source must be an ObservableValue. +Observable[combineLatest]([a, 42]);