Type guards et narrowing TypeScript : affiner les types

🏷️ Front-end 📅 12/04/2026 09:00:00 👤 Mezgani Said
Typescript Type Guards Type Narrowing Discrimination Safety
Type guards et narrowing TypeScript : affiner les types

Maîtriser les type guards et le type narrowing pour affiner les types et écrire du code TypeScript plus sûr.

Introduction aux Type Guards

Les Type Guards affinent le type d'une variable dans un bloc de code spécifique, permettant à TypeScript de déterminer le type exact dans chaque branche.

À retenir : Les Type Guards transforment un type large (union) en un type plus spécifique — c'est le narrowing.

typeof Guard

Le guard typeof permet de différencier les types primitifs :

function process(value: string | number) {
    if (typeof value === 'string') {
        console.log(value.toUpperCase()); // TypeScript sait que c'est une string
    } else {
        console.log(value + 10); // TypeScript sait que c'est un number
    }
}

instanceof Guard

Le guard instanceof permet de différencier les classes :

class Dog { bark() { console.log('Woof!'); } }
class Cat { meow() { console.log('Meow!'); } }

function pet(animal: Dog | Cat) {
    if (animal instanceof Dog) {
        animal.bark(); // Narrow à Dog
    } else {
        animal.meow(); // Narrow à Cat
    }
}

Custom Type Guards

Un type guard personnalisé utilise la syntaxe value is Type comme retour :

interface User { id: number; name: string; }
interface Admin extends User { permissions: string[]; }

// La syntaxe "user is Admin" informe TypeScript du résultat
function isAdmin(user: User): user is Admin {
    return 'permissions' in user;
}

const user: User | Admin = getUser();
if (isAdmin(user)) {
    console.log(user.permissions); // TypeScript accepte — narrowed à Admin
}

Discriminated Unions

Une propriété discriminante commune permet de distinguer les membres d'une union :

type Success = { status: 'success'; data: any };
type Failure = { status: 'error'; message: string };
type Result = Success | Failure;

function handle(result: Result) {
    if (result.status === 'success') {
        console.log(result.data);    // Narrowed à Success
    } else {
        console.log(result.message); // Narrowed à Failure
    }
}

Type Narrowing

TypeScript affine automatiquement les types dans les blocs conditionnels :

function example(x: string | number) {
    // Ici x est string | number
    // console.log(x.toUpperCase()); // Erreur : number n'a pas toUpperCase

    if (typeof x === 'string') {
        console.log(x.toUpperCase()); // OK : narrowed à string
    }
}

Patterns avancés

Le type never permet de vérifier l'exhaustivité d'un switch :

type Status = 'pending' | 'done' | 'error';

function handleStatus(status: Status): void {
    if (status === 'pending') {
        return;
    } else if (status === 'done') {
        return;
    } else if (status === 'error') {
        return;
    }
    // Si un cas est oublié, TypeScript signale une erreur ici
    const _exhaustive: never = status;
}
Note : Cette technique s'appelle exhaustiveness checking. Elle garantit que chaque cas de l'union est traité.

Conclusion

Les Type Guards et le narrowing sont des outils essentiels pour travailler avec les types unions en TypeScript. Ils permettent à TypeScript de déterminer le type exact dans chaque branche de ton code.

Combine typeof, instanceof, les guards personnalisés et les discriminated unions pour écrire du code TypeScript robuste et sans cast forcé.

À retenir : Préfère les discriminated unions et les custom type guards aux cast forcés (as). C'est plus sûr et plus explicite.