codelynx.dev
🇫🇷🇬🇧

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 :

TS
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.

TSX
interface Bird {
  color: string;
}

interface Bird {
  size: number;
}

const bird: Bird = { color: 'red', size: 14 };

Playground Link

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.

TSX
class DogClass {
  name: string = 'Dog';

  constructor(name: string) {
    this.name = name;
  }
}

interface DogExtend extends DogClass {
  color: string;
}

const dogExtend: DogExtend = { name: 'dog', color: 'red' };

Playground Link

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.

TSX
type DogNameUnion = 'Cookie' | 'Mathieu';

const test1: DogNameUnion = 'Cookie';
const test2: DogNameUnion = 'Test';

Playground Link

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.

TSX
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
};

Playground Link

Comme tu l'as remarqué, peu importe si c'est une interface ou un type.

Intersection

Il permet de merger deux type ensemble.

TSX
type Dog = {
  name: number;
};

interface Color {
  color: number;
}

type DogColor = Dog & Color;

Playground Link

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).

TSX
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.

TSX
interface DogInterface {
  dog: [string, number];
  tuples: 'a' | 'b';
  intersections: { a: 2 } & { b: 3 };
}

Playground Link

Je vois un peu une interface comme un type obligatoire sous forme de { } mais qui peut contenir des types.

TSX
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 :

TSX
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.

TSX
interface ArrayOfString {
  [index: number]: string;
}

const test: ArrayOfString = ['b', 'o', 'b'];

test.map((a) => a); // Property 'map' does not exist on type 'ArrayOfString'

Playground Link

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 :

YAML
'@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.

NextReact

Cours NextJS gratuit

Accède à des exercices, des vidéos et bien plus sur NextJS dans la formation "NextReact" 👇