Narrowing

Qu'est-ce que le Narrowing dans TypeScript

Le narrowing sert à afiner (rendre plus précis) un type, ce qui est souvent pratique dans le cas d'une union de types.

Typeof type guards

En JS il y existe l'opérateur typeof, il est également disponible avec TypeScript et voici la liste des valeurs que TypeScript reconnait :

typeof x == "string";
typeof x == "number";
typeof x == "bigint";
typeof x == "boolean";
typeof x == "symbol";
typeof x == "undefined";
typeof x == "object";
typeof x == "function";

Ça peut être utile de connaitre ces valeurs pour faire du narrowing.

Exemples

Fonction recevant une union simple

Créons une fonction btcToUsd qui permet de convertir des BTC en $, elle accepte soit un nombre soit un objet en paramètre.

On peut faire du narrowing en testant le type de l'argument :

type Account = {
    address: string,
    btc: number
}

function btcToUsd(btc: number | Account): number {
    if(typeof btc === 'object') {
        return btc.btc * 22000;
    }
    return btc * 22000;
}

btcToUsd(0.15);
btcToUsd({address: 'bc1xxx', btc: 0.2});

On vient d'aider TypeScript à determiner le type de l'argument : à l'intérieur de la condition on sait que le type est forcément un Acount et non pas un nombre.

On aurait aussi pu faire l'inverse (tester si c'est un nombre).

Fonction recevant plusieurs objets ou type similaires

Le cas précédent était plutôt simple, cependant il y a des situations où la fonction peut reçevoir une union d'objets différents ou types ayant des propriétés communes.

Comme TypeScript ne permet pas d'utiliser typeof sur de tels types directement, voici comment faire :

type BtcAccount = {
    address: string,
    btc: number
}

type EthAccount = {
    address: string,
    eth: number
}

type AvaxAccount = {
    address: string,
    avax: number
}

function getBalance(acc: BtcAccount | EthAccount | AvaxAccount): number {
    if('btc' in acc) {
        return acc.btc;
    } else if('eth' in acc) {
        return acc.eth;
    } else if('avax' in acc) {
        return acc.avax;
    } else {
        throw new Error('Invalid account');
    }
}

Comme on ne pouvait pas utiliser typeof acc == BtcAccount directement par exemple, on peut faire du narrowing en testant qu'un objet a une propriété spécifique.

Conclusion

Il existe de nombreuses situations différentes et nous n'allons pas toutes les traiter mais ça devrait déjà vous aider si jamais vous avez à faire du narrowing.