Skip to content

Array.forEach incorrectly narrows the inferred type when elements are annotated with union typeΒ #55201

@shogunsea

Description

@shogunsea

Bug Report

πŸ”Ž Search Terms

Array.forEach
union type

πŸ•— Version & Regression Information

all versions

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about array and foreach

⏯ Playground Link

Playground link with relevant **code**

πŸ’» Code

interface FooValue {
  value: string;
}

interface BarValue {
  value: string;
  name: string;
}

interface Props {
  values: BarValue[] | FooValue[];
}

export function foo({ values }: Props) {
  return values.forEach((value) => value);
}

πŸ™ Actual behavior

The inferred type of value is simply FooValue:
image

πŸ™‚ Expected behavior

But really it should be FooValue | BarValue, which is correct when using .map:
image

I suspect this issue happens because forEach isn't annotated with generic return type U while map is:

TypeScript/src/lib/es5.d.ts

Lines 1235 to 1246 in 250065e

/**
* Performs the specified action for each element in an array.
* @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
forEach(callbackfn: (value: T, index: number, array: readonly T[]) => void, thisArg?: any): void;
/**
* Calls a defined callback function on each element of an array, and returns an array that contains the results.
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any): U[];

In fact after changing forEach to forEach<U> fixes this problem locally:
image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions