Peut-on étendre les types en TypeScript ? Plongeons dans les performances et l'optimisation
Bonjour, ami ! Si tu as déjà écrit en TypeScript, tu t'es sûrement demandé : "Peut-on étendre les types comme on le fait avec les interfaces ?" Cette question sur Stack Overflow a recueilli des milliers de vues, et ce n'est pas pour rien. À première vue, tout semble simple : les interfaces s'étendent avec extends, et les types avec l'intersection (&). Mais il y a des nuances, et elles impactent directement les performances et la lisibilité du code.
Aujourd'hui, nous allons voir comment étendre les types en TypeScript, en quoi cela diffère des interfaces, et comment éviter les pièges lors de l'optimisation. C'est parti !
Type vs Interface : le combat éternel
Avant de parler d'extension, rappelons la différence. Les interfaces sont des "contrats" qui peuvent être déclarés plusieurs fois (fusion déclarative), tandis que les types sont des alias (pseudonymes) pour n'importe quelle forme. Les deux peuvent être étendus, mais de manière différente.
Comment étendre les interfaces ?
C'est simple :
interface Animal { name: string;}
interface Bear extends Animal { honey: boolean;}
const bear: Bear = { name: 'Winnie', honey: true };Simple, élégant, ça fonctionne. Mais que faire si on veut la même chose pour un type ?
Comment étendre les types ?
Les types ne supportent pas directement extends. On utilise à la place l'opérateur d'intersection (&) :
type Animal = { name: string;};
type Bear = Animal & { honey: boolean };
const bear: Bear = { name: 'Winnie', honey: true };Cela semble similaire, mais il y a des pièges. Analysons-les avec des exemples.
Performances : qu'est-ce qui est plus rapide ?
La question à un million : le choix entre interface extends ou type & affecte-t-il la vitesse de compilation et d'exécution ? Spoiler : oui, mais pas comme tu le penses.
Compilation : les interfaces gagnent
TypeScript compile les interfaces plus rapidement que les intersections de types. Pourquoi ? Les interfaces sont des structures "plates" que le compilateur peut mettre en cache et traiter linéairement. Les intersections de types sont des constructions complexes qui peuvent nécessiter un déploiement récursif.
// Interface - rapideinterface A { a: string }interface B extends A { b: number }
// Type - plus lenttype A = { a: string }type B = A & { b: number }Dans les grands projets avec des centaines de types, la différence peut être notable. Mais si tu as 10 à 20 entités, tu ne la ressentiras pas.
Exécution : aucune différence
TypeScript est une surcouche de JavaScript. À l'exécution, il n'y a ni interfaces ni types. Ils n'existent qu'à l'étape de développement. Par conséquent, le choix n'affecte pas du tout la vitesse d'exécution du programme.
Quand les types sont-ils meilleurs que les interfaces ?
Malgré la lenteur potentielle de la compilation, les types sont indispensables dans certains scénarios :
- Union types — union de types (par exemple,
string | number) - Conditional types — types conditionnels (par exemple,
T extends string ? 'yes' : 'no') - Mapped types — transformation de propriétés (par exemple,
{ [K in keyof T]: boolean })
Les interfaces ne peuvent pas gérer cela. Mais si tu as simplement besoin d'étendre un objet, les interfaces sont les meilleures.
Optimisation : comment ne pas se tirer une balle dans le pied
Passons maintenant à la partie la plus intéressante. Comment écrire des types extensibles sans ralentir la compilation et sans embrouiller tes collègues ?
Règle 1 : Utilise les interfaces pour les objets, les types pour tout le reste
C'est la règle d'or. Si tu décris la forme d'un objet, utilise interface. Si tu as besoin d'un union, d'une intersection, ou de magie avec les génériques, utilise type.
// Bieninterface User { id: number; name: string;}
type Status = 'active' | 'inactive';type Admin = User & { role: 'admin' };<