Retour • 03/06/2022
Type VS Interface en TypeScript
Écris par Melvyn Malherbe le 03/06/2022
Tu l'as déjà eu la remarque : "Nom mais utilise une interface ici !" ou "Pourquoi tu utilises pas un type ?"... Moi oui, et je me suis demandé : C'est quoi la différence ?
Un choix se base sur des arguments... et pour avoir des arguments il faut les chercher. Tu es au bonne endroit car j'ai fais ce travail pour toi et les résultats sont... surprenants.
En TypeScript il existe des types afin de donner un type à vos variables et fonctions. Pour déclarer un type, TypeScript propose deux syntaxes :
type User = {
username: string;
password: string;
};
interface User {
username: string;
password: string;
}
A première vue, il n'y a aucune différence. Dans ce petit article, on va découvrir et énumérer les différences entre les deux syntaxes.
Merge des declarations
La grosse différence entre les deux, c'est que tu peux redéclarer une interface et TypeScript va automatiquement les merger ensemble.
interface Bird {
color: string;
}
interface Bird {
size: number;
}
const bird: Bird = { color: 'red', size: 14 };
Tu peux tester dans le playground, mais le code ci-dessus est valide. Par contre, si tu
remplace interface
par type
tu vas avoir une erreur, comme tu peux le voir ici.
Mon avis personnel est que le type est plus pratique dans ce cas. Tu n'as pas envie de pouvoir redéclarer le même Type plusieurs fois. C'est pas très pertinant.
Extends classes
Si tu utilises les classes JavaScript, les interfaces possèdent des avantages.
Notamment le fait de pouvoir étendre une classe.
class DogClass {
name: string = 'Dog';
constructor(name: string) {
this.name = name;
}
}
interface DogExtend extends DogClass {
color: string;
}
const dogExtend: DogExtend = { name: 'dog', color: 'red' };
extends
permet de rajouter un champ à une classe et de"créer un nouveau type. J'ai cherché
et je ne crois pas que c'est possible avec les type
. Mais j'avoue que je n'utilise
jamais cette feature.
La flexibilité
Type est beaucoup plus flexible. Tu peux tout mettre dans un type, il n'a pas trop de limite.
Union
Avec un type, tu peux facilement déclarer des tuples. Les tuples sont vraiment sous-cotés et pas assez utilisés alors qu'ils sont surpuissants.
type DogNameUnion = 'Cookie' | 'Mathieu';
const test1: DogNameUnion = 'Cookie';
const test2: DogNameUnion = 'Test';
Dans mes composants React, pour un bouton par exemple je vais utiliser les tuples
pour définir le variant de mon bouton : type ButtonVariant = "primary" | "secondary"
.
Il a aussi un autre usage :
On peut définir des types un peu magiques pour du POO. Ils vont définir un certain type
en fonction de certains champs. Dans cet exemple, si le animalType
est "Dog" il faudra
avoir une size
, et si c'est "Cat" il faudra avoir une color
.
type Dog = {
animalType: 'Dog';
size: number;
};
interface Cat {
animalType: 'Cat';
color: string;
}
type Animal = Dog | Cat;
const cat: Animal = {
animalType: 'Cat',
color: 'red',
};
const fakeCat: Animal = {
animalType: 'Dog',
color: 'red', // erreur, dog ne peut pas avoir une couleur mais une taille
};
Comme tu l'as remarqué, peu importe si c'est une interface ou un type.
Intersection
Il permet de merger deux type ensemble.
type Dog = {
name: number;
};
interface Color {
color: number;
}
type DogColor = Dog & Color;
Encore une fois, on est obligé d'utiliser un type pour faire le &
mais il peut merger
des interfaces comme des types.
Tuples
Les tuples permettent de définir le type d'un tableau (très grossièrement).
type Dog = [string, number];
const response: Dog = ['Cookie', 12];
Il est très utile car, retourner des variables sous forme de tableau permet de
les nommer comme tu peux, comme en React, ils utilisent un tuple pour définir le type de retour du useState
qui
retourne [state, setState]
.
En React, les tuples sont très utilisés pour avoir des props comme color: "red" | "blue"
. Retrouve plus d'informations sur ma formation React ici.
Mais les interfaces le peuvent aussi
Finalement, une interface ne peut pas être assignée à ce genre de type, mais elle peut totalement avoir ce genre de type à l'intérieur de celle-ci.
interface DogInterface {
dog: [string, number];
tuples: 'a' | 'b';
intersections: { a: 2 } & { b: 3 };
}
Je vois un peu une interface comme un type obligatoire sous forme de { }
mais
qui peut contenir des types.
interface Interface {
b: /* any type, tuples, intersection... */;;
}
Mais elle n'as pas le luxe de pouvoir être assignée à une propriété non standard.
Les tableaux
Si tu veux typer un tableau de string, comme ["Cookie", "Mathieu"]
, le type
a une syntaxe
encore une fois beaucoup plus élégante :
type ArrayOfString = string[];
interface ArrayOfString {
[index: number]: string;
}
Les deux types ci-dessus sont valides pour notre tableau composé de "Cookie" et "Mathieu", mais l'interface à un autre problème.
interface ArrayOfString {
[index: number]: string;
}
const test: ArrayOfString = ['b', 'o', 'b'];
test.map((a) => a); // Property 'map' does not exist on type 'ArrayOfString'
Quand tu utilise l'interface, tu n'utilises pas le type Array
... Tu crées une sorte
de nouveau type pour les tableaux, qui définit une clé comme étant number.
Merci à @Console_buche de m'avoir fait remarquer ce détail... qui a son importance.
Tu devrais utiliser quoi ?
Évidement, ici, c'est un avis personnel. De grands sages m'ont appris à essayer de toujours être le maximum consistant.
C'est à dire ne pas s'amuser à utiliser une fois un type, une fois une interface et mixer les deux dans ma codebase.
En raison de leurs différences, de la flexibilité du type
, sa syntaxe des tableaux
ainsi que de sa simplicité, j'ai choisi de toujours utiliser le type.
Et comme chaque fois que tu fais un choix, tu dois ajouter une règle eslint voici la mienne :
'@typescript-eslint/consistent-type-definitions':
- error
- type
Donc je suis obligé de toujours utiliser des types, et si j'utilise une interface, boom 💥, eslint me corrige automatiquement.
Conclusion
On a vu toutes les différences entre un type
et une interface
en TypeScript et
on sait maintenant laquelle choisir. Je recommande personnellement le type
et je vous
ai même partagé une petite règle eslint.
Dans ma formation NextjS, on commence avec un module TypeScript
qui va t'aider à adopter les bonnes pratiques.