You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unable to call map on array of variable depth #35045
I'm trying to write a function which transforms the coordinates of any type of GeoJSON geometry. The coordinates of a Geometry can be either number[] (a Point), number[][] (a LineString), number[][][] (a Polygon) or number[][][][] (a MultiPolygon). I have a recursive implementation which works but does not type check.
TypeScript Version: 3.7.2
Search Terms:
2349
Code
typePosition=number[];functionisNestedArray<Textendsany[]>(x: T|T[]): x is T[]{returnArray.isArray(x[0]);}exportfunctiontransformCoordArray(coords: Position|Position[]|Position[][],fn: (pt: Position)=>Position): any[]{if(isNestedArray(coords)){coords;// type is Position[] | Position[][]returncoords.map(c=>transformCoordArray(c,fn));// ~~~ This expression is not callable.}else{coords;// type is Positionreturnfn(coords);}}
This expression is not callable.
Each member of the union type '
(<U>(callbackfn: (value: Position, index: number, array: Position[]) => U, thisArg?: any) => U[])
| (<U>(callbackfn: (value: Position[], index: number, array: Position[][]) => U, thisArg?: any) => U[])
' has signatures, but none of those signatures are compatible with each other.(2349)
The call is valid because c's type should be Position | Position[], which is assignable to Position | Position[] | Position[][].
Interestingly, I can make the error go away by assigning to a new variable which has a broader type ((Position | Position[])[]):
(playground)
exportfunctiontransformCoordArray(coords: Position|Position[]|Position[][],fn: (pt: Position)=>Position): any[]{if(isNestedArray(coords)){coords;// type is Position[] | Position[][]constc2: (Position|Position[])[]=coords;returnc2.map(c=>transformCoordArray(c,fn));// ok}else{coords;// type is Positionreturnfn(coords);}}
This new behavior only kicks in when at most one type in the union has multiple overloads, and at most one type in the union has a generic signature. That means methods on number[] | string[] like map (which is generic) still won’t be callable.
While methods like push are obviously problematic for number[] | string[], it looks safe to allow methods like map or every for readonly number[] | readonly string[]. Has this (special treatment of read-only arrays when merging union signatures) already been discussed in some issue?
Even though node.properties are correctly inferred to be of type ObjectLiteralElementLike[] | Property[] (because both estree and @typescript-eslint/typescript-estree define ObjectExpression, not exactly compatible though), p in the every callback is of type any. 😢
I think it's inherently bad idea to rely on compiler to figure out the output of the transformCoordArray() meanwhile coords: ... argument varies.
I think much better way either to create overloads (checkout playground):
Activity
jcalz commentedon Nov 12, 2019
Duplicate of #7294 (see comment). Related to #29011.
Mentioned in documentation as caveats for improved behavior for calling union types:
thorn0 commentedon Nov 12, 2019
While methods like
pushare obviously problematic fornumber[] | string[], it looks safe to allow methods likemaporeveryforreadonly number[] | readonly string[]. Has this (special treatment of read-only arrays when merging union signatures) already been discussed in some issue?thorn0 commentedon Nov 15, 2019
We are trying to gradually introduce type checking in the Prettier project and facing this issue.
Prettier works with ASTs that come from different parsers.
E.g. suppose we have an object
nodeof typeimport('estree').Node | import('@typescript-eslint/typescript-estree').TSESTree.Nodeand code like this:Even though
node.propertiesare correctly inferred to be of typeObjectLiteralElementLike[] | Property[](because bothestreeand@typescript-eslint/typescript-estreedefineObjectExpression, not exactly compatible though),pin theeverycallback is of typeany. 😢hinell commentedon Nov 16, 2019
I think it's inherently bad idea to rely on compiler to figure out the output of the
transformCoordArray()meanwhilecoords: ...argument varies.I think much better way either to create overloads (checkout playground):
or assert
coords:jcalz commentedon May 27, 2021
Fixed by #42620 I think?
danvk commentedon May 27, 2021
Yep! The error in the example in the original issue goes away with 4.3: playground