typescript, Array.prototype.map() har felet 'expression is not callable' när den kallade arrayen är en union med en tom array


Jag har en typalias Data som är en union av två datastrukturer -- ena innehåller en array som inte är tom medan den andra är tom:

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)

Men när jag försöker anropa Array.prototype.map() på arrayen får jag ett felmeddelande som säger: "detta uttryck kan inte anropas"

Ovanstående utdrag kan hittas här

Jag märker att jag kan eliminera typfelet om jag definierar aliaset för Data som snitt:

type Data = DataNotEmptyArray & DataEmptyArray

eller helt enkelt inte förena med DataEmptyArray

type Data = DataNotEmptyArray

Kan du vänligen förklara varför union med en tom Array är ett problem? Vad betyder det när det står "uttrycket är inte anropbart"? Tack!!

Svar #1

Alternativ 1

Uppdatera TypeScript till 4.2+ (Jag rekommenderar den senaste versionen)

Detta kommer att fungera för .map, men det kommer inte att fungera för .reduce, .every, .find och .filter

(Redigera 2023-05-17: .every, .find och .filter bör åtgärdas i nästa release TypeScript 5.2?)

Från och med maj 2023 finns det ett öppet ärende om .reduce-fallet: https://github.com/microsoft/TypeScript/issues/44063

Alternativ 2

Under tiden kan du lägga till as any[] för att bli av med felet:
För .map:

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

För .reduce:

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

Mer information

Problemet för .map:
https://github.com/microsoft/TypeScript/issues/36390

Problemet för .every, .find och .filter-fallen:
https://github.com/microsoft/TypeScript/issues/44373

Relaterade artiklar