Hur kan jag kontrollera typen på en Array i Typescript?


Jag har en funktion med följande signatur

public async sequenceAnimations(animations: AnimationPlayer[] | AnimationTracker[]): Promise<any>

I själva funktionen vill jag förgrena baserat på om det är en AnimationPlayer-array eller en AnimationTracker-array, så jag försökte detta:

let mappedAnimations = animations;
if (animations instanceof Array<AnimationTracker>) {
    mappedAnimations = animations.map(anim => anim.animationPlayer)
}

Som du kan se försöker jag tillåta att anroparen skickar antingen en AnimationPlayer-array eller en AnimationTracker-array som har en instans av animationPlayer. Men jag får ett fel när jag kontrollerar instanceof arrayen med en typ

Högersidan av ett 'instanceof'-uttryck måste vara av typen 'any' eller av en typ som kan tilldelas typen 'Function'.

Autokompletteringen registrerar inte heller typen av arrayen i if-blocket, så jag antar att jag inte kan kontrollera arraytypen på det här sättet.

Vad är det korrekta sättet att avgöra vilken typ av array som skickas?

Svar #1

Du kan inte använda instanceof med en generisk typ med typparametrar. Efter kompilation tas alla generiska typer bort, så animations instanceof Array<AnimationTracker> skulle bli animations instanceof Array vilket inte skulle göra det du förväntar dig.

Eftersom arrayer i Javascript inte är typade finns det inget inbyggt sätt att skilja mellan AnimationPlayer[] och AnimationTracker[] och om arrayerna är tomma är de vid körning verkligen oskiljbara. Du kan dock skapa ett eget typvakt som använder det första icke-nollvärdet i arrayen för att bestämma typen. För tomma arrayer skulle detta alltid returnera false, men det kan vara en okej lösning i de flesta fall:

function isArrayOf<T>(array:any[], cls: new (...args: any[]) => T) : array is T[] {
    for(let item of array) {
        if(item != null) return  item instanceof cls;
    }
    return  false;
}
async function sequenceAnimations(animations: AnimationPlayer[] | AnimationTracker[]): Promise<any> {
    let mappedAnimations = animations;
    if (isArrayOf(animations, AnimationTracker)) {
        // animations is AnimationTracker[]
        mappedAnimations = animations.map(anim => anim.animationPlayer);
    }
}

Svar #2

Kort svar: Du kan inte.

Huvudidén med TypeScript är att lägga till typer vid kompileringstid och generera vanlig JavaScript-kod efter kompileringen. JavaScript i sig har inget stöd för avancerad typkontroll och därför är ditt enda alternativ att göra duck-typing vid körning.

Relaterade artiklar