Can't call `forEach` on `number[] | ReadonlyArray<never>`
Opened this issue · 2 comments
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.
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, @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.
Thanks, I'll mark this as a duplicate of that issue.