Menu

extends en TypeScript : étendre une interface ou un type

Le mot-clé extends construit un type à partir d'un autre. Apprenez à étendre une interface (une seule ou plusieurs), à étendre un alias de type avec &, à redéfinir le type d'une propriété, à remplacer des propriétés avec Omit, et ce que signifie extends dans les classes, les contraintes génériques et les types conditionnels.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

interface Child extends Parent crée une nouvelle interface qui possède tous les membres de Parent, plus les siens. Les valeurs du type enfant peuvent être utilisées partout où le parent est attendu, puisqu'elles ont toutes ses propriétés.

Le même mot-clé apparaît à quatre endroits en TypeScript, avec des sens voisins mais différents :

OùExempleSignification
Interfaceinterface Dog extends Animal {}hériter des membres du parent
Classeclass Dog extends Animal {}hériter des champs et des implémentations de méthodes (JavaScript à l'exécution)
Paramètre générique<T extends Animal>contrainte : l'argument de type doit être assignable à Animal
Type conditionnelT extends string ? A : Btest : T est-il assignable à string ?

Étendre plusieurs interfaces

Listez plusieurs parents après extends, séparés par des virgules. Le résultat possède les membres de tous :

interface Identified {
  id: number;
}

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface Post extends Identified, Timestamped {
  title: string;
}

// Post requires id, createdAt, updatedAt and title

Si deux parents déclarent la même propriété avec des types qui ne sont pas identiques, l'enfant provoque l'erreur TS2320 (Interface 'Post' cannot simultaneously extend types 'A' and 'B'), sauf si l'enfant redéclare cette propriété avec un type assignable aux deux.

Étendre un alias de type

Un alias type n'a pas de clause extends. Vous le complétez avec une intersection, &, qui combine les membres des deux côtés. Une interface peut aussi étendre directement un alias de type objet, et un alias de type peut faire l'intersection avec une interface : les deux sortes se mélangent librement.

Les deux approches se comportent de la même façon jusqu'à ce que des propriétés entrent en conflit : extends signale une propriété contradictoire au niveau de la déclaration, alors que & la transforme en silence en never. La comparaison se trouve sur la page interface ou type, et les détails de & sur la page des types intersection.

Redéfinir le type d'une propriété

Une interface enfant peut redéclarer une propriété du parent, mais seulement avec un type assignable à celui du parent. Affiner string en un littéral, ou retirer null d'une union, est autorisé. Changer le type en quelque chose sans rapport, non :

index.ts(13,11): error TS2430: Interface 'Label' incorrectly extends interface 'Shape'.
  Types of property 'kind' are incompatible.
    Type 'number' is not assignable to type 'string'.

Cette règle existe pour qu'un Label puisse toujours être utilisé partout où un Shape est attendu. Si kind pouvait être un nombre, du code écrit pour Shape qui appelle shape.kind.toUpperCase() casserait.

Remplacer des propriétés avec Omit

Quand vous avez vraiment besoin d'un autre type pour une propriété héritée, retirez-la d'abord du parent avec Omit, puis déclarez la nouvelle. Un cas courant est la conversion de données d'API, où les identifiants et les dates arrivent sous forme de chaînes :

Omit<ApiUser, "id" | "createdAt"> vaut { name: string }, donc les nouveaux id et createdAt n'entrent en conflit avec rien. Pick, Partial et les autres types utilitaires s'étendent de la même façon.

extends avec les classes

class Dog extends Animal est l'héritage de classes de JavaScript et, contrairement au extends des interfaces, il existe à l'exécution : Dog reçoit le constructeur, les champs et les implémentations de méthodes d'Animal, et new Dog(...) instanceof Animal vaut true. TypeScript ajoute des vérifications de types par-dessus, comme le mot-clé override :

Une classe ne peut étendre (extends) qu'une seule classe, mais elle peut implémenter (implements) autant d'interfaces qu'elle veut. implements ne copie rien : il vérifie seulement que la classe déclare ce qu'exige l'interface. super, les membres protected et override sont présentés dans héritage.

extends dans les génériques et les types conditionnels

Entre chevrons, extends est une contrainte. <T extends { length: number }> accepte tout argument de type qui possède une length numérique, et permet au corps de la fonction d'utiliser .length. Dans un type conditionnel, T extends U ? X : Y demande si T est assignable à U.

Dans les deux cas, extends signifie « est assignable à », la même relation que vérifie la forme interface. Les contraintes sont traitées en détail dans contraintes génériques, et les tests au niveau des types dans types conditionnels.

Questions fréquentes

Comment étendre une interface en TypeScript ?

Écrivez interface Child extends Parent { ... }. L'enfant possède toutes les propriétés du parent, plus celles qu'il déclare. Pour étendre plusieurs interfaces d'un coup, séparez-les par des virgules : interface C extends A, B { ... }.

Comment étendre un alias de type en TypeScript ?

Un alias de type ne peut pas utiliser extends ; combinez plutôt les types avec une intersection : type Admin = User & { permissions: string[] }. Une interface peut aussi étendre directement un alias de type objet : interface Admin extends User { permissions: string[] }.

Peut-on redéfinir le type d'une propriété en étendant une interface ?

Seulement avec un type compatible, plus étroit : kind: "circle" peut remplacer kind: string. Un type différent, comme number pour une propriété string, provoque l'erreur TS2430 (Interface 'X' incorrectly extends interface 'Y'). Pour remplacer une propriété par un type sans rapport, retirez-la d'abord : interface User extends Omit<ApiUser, "id"> { id: number }.

Quelle est la différence entre extends et implements ?

extends hérite : une interface reçoit les membres du parent, et une classe reçoit les champs et les implémentations de méthodes de la classe parente. implements ne fait que vérifier : une classe qui implémente une interface doit déclarer elle-même chaque membre, et ne gagne rien de l'interface.

Que signifie extends dans un générique comme <T extends string> ?

C'est une contrainte, pas un héritage. T extends string signifie que l'argument de type doit être assignable à string, vous pouvez donc utiliser les méthodes de chaîne sur T dans la fonction. Dans un type conditionnel, T extends U ? X : Y est un test : si T est assignable à U, le résultat est X, sinon Y.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER