Skip to content

Can't call forEach on number[] | ReadonlyArray<never> #16716

Closed

Description

TypeScript Version: nightly (2.5.0-dev.20170623)

Code

const empty: ReadonlyArray<never> = [];
function f(nums: number[] | undefined) {
    const a = nums || empty;
    a.forEach(n => {});
}

Expected behavior:

No error.

Actual behavior:

4     a.forEach(n => {});
      ~~~~~~~~~~~~~~~~~~

src/a.ts(4,5): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((callbackfn: (value: never, index: number, array: ReadonlyArray<never>) => void, thisArg?: any) ...' has no compatible call signatures.


4     a.forEach(n => {});
                ~

src/a.ts(4,15): error TS7006: Parameter 'n' implicitly has an 'any' type.

Activity

  1. jcalz commented on Jun 23, 2017

    @jcalz
    Contributor

    I think the first error message is from the same issue reported in #16644:

    var a = [1, 2, 3];
    (a as number[]).filter(x => typeof x === 'string'); // fine
    (a as (number | string)[]).filter(x => typeof x === 'string'); // fine
    (a as number[] | (number | string)[]).filter(x => typeof x === 'string'); // error!
    
    var b = [];
    (b as number[]).forEach(x => console.log(x)); // fine
    (b as never[]).forEach(x => console.log(x)); // fine
    (b as number[] | never[]).forEach(x => console.log(x)); // error!

    In that issue, Jakub Korzeniowski (@kujon) was asking that A[] | B[] should reduce to (A|B)[] which is consistent with the way TypeScript currently treats arrays as covariant in their content type. But possibly-mutable arrays should be invariant in their type, so maybe that fix would be going farther in the wrong direction.

    The other way of looking at this issue is:

    type Overloaded<A, B> = {
      (a: A): void;
      (b: B): void;
    }
    function exploded<A, B>(f: Overloaded<A,B>, ab: A | B): void {
      f(ab); // error!
      var isA: (ab) => ab is A;
      isA(ab) ? f(ab) : f(ab); // workaround, no error 
    }

    TypeScript can't take an overloaded or generic function and realize that if it can resolve a parameter of type A and separately resolve a parameter of type B, then it "should" be able to handle a parameter of type A|B. But here be dragons.

  2. ghost closed this as completedon Jun 23, 2017
  3. ghost added
    DuplicateAn existing issue was already created
    on Jun 23, 2017
  4. locked and limited conversation to collaborators on Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions