Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typescript, Array.prototype.map() has error 'expression is not callable' when the callee array is union with empty array

Tags:

typescript

I have a type alias Data which is a union of two data structure -- one contain array that is not empty while the other is empty:

const dataEmptyArray = { data: [] }
const dataNotEmptyArray = { data: [1, 2, 3] }

type DataEmptyArray = typeof dataEmptyArray
type DataNotEmptyArray = typeof dataNotEmptyArray

type Data = DataNotEmptyArray | DataEmptyArray // <--- union here

function foo(arg:Data) {
  if (arg && arg.data && Array.isArray(arg.data)) {
    return arg.data.map( (d:(never|number)) => d)
    //              ^^^<--------- this expression is not callable   
  } else {
    return 'no data'
  }
}

const result = foo(dataEmptyArray)

However, when I try to call Array.prototype.map() on the array I have an error said: "this expression is not callable"

The above snippet can be found here

I notice, I can eliminate the type error if I define the alias of Data as intersection:

type Data = DataNotEmptyArray & DataEmptyArray

or simply don't union with DataEmptyArray

type Data = DataNotEmptyArray

Could you please explain why union with empty Array is a problem ? What does it mean when it said "the expression is not callable"? thanks!!

like image 537
apollo Avatar asked Nov 27 '25 21:11

apollo


1 Answers

Option 1

Update typescript to 4.2+ (I recommend the latest version)

This will work for .map, but it will not work for .reduce, .every, .find and .filter

(Edit 2023-05-17: .every, .find and .filter should be fixed in the next release TypeScript 5.2?)

As of May 2023 there is an open issue about the .reduce case: https://github.com/microsoft/TypeScript/issues/44063

Option 2

In the meantime you can add as any[] to get rid of the error:
For .map:

const arr: number[] | string[] = [];
// Add as any[]
(arr as any[]).map((a: number | string, index: number) => { 
    return index
});

For .reduce:

const arr: number[] | string[] = [];
// Add as any[]
(arr as any[]).reduce((acc: number | string, val: number|string) => { 
    return `${acc} ${val}`
},'');

More information

The issue for .map:
https://github.com/microsoft/TypeScript/issues/36390

The issue for the .every, .find and .filter cases:
https://github.com/microsoft/TypeScript/issues/44373

like image 158
sazzy4o Avatar answered Dec 02 '25 18:12

sazzy4o



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!