Skip to content

Incorrect type inferred in Array reduce function #39259

Closed
@jimador

Description

@jimador

TypeScript Version: 3.9.2/3.8.3/Nightly

Search Terms:

  • array reduce
  • reduce type inference

Code

type SomeParamType = { hello: string; world: string; }

function brokenInference<T>(someArg: SomeParamType): boolean {
  type KeyType = keyof typeof someArg
  const keys = (Object.keys(someArg) as KeyType[])

  return keys.reduce((previousValue, currentValue) => {
      const result = currentValue == 'hello world'
      return previousValue && result
    }, true)
}

function correctInference(someArg: SomeParamType): boolean {
  type KeyType = keyof typeof someArg
  const keys = (Object.keys(someArg) as KeyType[])
  
  return keys.reduce<boolean>((previousValue, currentValue) => {
      const test = someArg[currentValue] === 'hello world'
      return previousValue && test
    }, true)
}

function correctInference1(someArg: SomeParamType): boolean {
  type KeyType = keyof typeof someArg
  const keys = (Object.keys(someArg) as KeyType[])
  const value = keys.reduce((previousValue, currentValue) => {
      const test = someArg[currentValue] === 'hello world'
      return previousValue && test
    }, true)
  return value
}

Expected behavior:
The type of keys.reduce(...) should be inferred as boolean

Actual behavior:
The type of keys.reduce(...) is inferred as keyof T.

Notes
From what I can tell, there are currently 3 definitions for Array.reduce.

reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U;

When the reduce function is called from const result = (item: T) => keys.reduce TS appears to be inferring the signature of reduce to be reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U;. When the function is inlined, the signature of reduce is inferred to be reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T;.

Question:

Is this signature needed?

reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T

This signature would seem to satisfy those constraints.

reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U

Playground Link:
3.9.2
3.8.3
Nightly

Related Issues:
This issue appears to be similar to #33886, #25454, #33886, #29604

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions