ReactiveX/rxjs · error · TypeError

A combineLatest projection requires an array of observable v

Error message

A combineLatest projection requires an array of observable values

What it means

In RxJS Next's combineLatest, a projection (result selector) function is only supported with the array form of the operator. The dictionary form computes values keyed by property name, so a positional project callback has no defined meaning and throws.

Source

Thrown at packages/rxjs/src/combine-latest.ts:81

    );
    return project === undefined ? (combined as any) : combined[map]((values) => project(...values));
  }

  const actualSources: readonly ObservableValue<any>[] | { [key: string]: ObservableValue<any> } = Array.isArray(sources)
    ? [...sources]
    : { ...sources };

  if (isSourceArray(actualSources)) {
    const combined = this[combine](
      actualSources.map((source) => ({
        source,
        requireFirstValue: requireAllValues,
      }))
    );
    return project === undefined ? (combined as any) : combined[map]((values) => project(...values));
  } else {
    if (project !== undefined) {
      throw new TypeError('A combineLatest projection requires an array of observable values');
    }
    const keys = Object.keys(actualSources);

    return this[combine](
      keys.map((key) => ({
        source: actualSources[key]!,
        requireFirstValue: requireAllValues,
      }))
    )[map]((values) => Object.fromEntries(keys.map((key, i) => [key, values[i]]))) as any;
  }
}

function isSourceArray(sources: any): sources is readonly ObservableValue<any>[] {
  return Array.isArray(sources);
}

View on GitHub (pinned to 54796b38a5)

Solutions

  1. Drop the project function and map afterwards: combineLatest({a, b}).pipe(map(({a, b}) => a + b))
  2. If you need a projection, switch to the array form: combineLatest([obsA, obsB], (a, b) => a + b)
  3. Remove resultSelector usage left over from RxJS <=7 migrations

Example fix

// before
combineLatest({a: obsA, b: obsB}, (a, b) => a + b);
// after
combineLatest({a: obsA, b: obsB}).pipe(map(({a, b}) => a + b));
Defensive patterns

Strategy: validation

Validate before calling

if (typeof configOrProject === 'function' && !Array.isArray(sources)) {
  // project only valid with array form — restructure the call
}

Type guard

const isSourcesObject = (v: unknown): v is Record<string, Observable<any>> =>
  typeof v === 'object' && v !== null && !Array.isArray(v);

Prevention

When it happens

Trigger: combineLatest({a: obsA, b: obsB}, (a, b) => a + b) — passing a function as the second argument alongside a sources object.

Common situations: Porting RxJS 6/7 code that used the deprecated resultSelector overload, or copy-pasting the array-form project callback onto the object form during migration.

Related errors


AI-assisted analysis of ReactiveX/rxjs@54796b38a5 (2026-08-28). Data as JSON: /api/errors/d98c2aa0723d5415. Report an issue: GitHub.