Types Aliases ou Interfaces ? on découvre comment les utiliser et surtout quand.
Jour de match !
Quelle est la différence entre les Types Aliases et les Interfaces ? Pourquoi utiliser plutôt l'un que l'autre ?
Pour cette deuxième journée, l'affrontement s'annonce une nouvelle fois palpitant. Mais avant de les confronter, on va d'abord en apprendre un peu plus sur chacun d'eux.
Les Types Aliases sont tout simplement des noms donnés à des types. Et comme un exemple vaut mieux qu'un long discours, voici comment on déclare un Type Alias :
type Car = {
brandName: string
model: string
}
On peut aussi utiliser un Type Aliases pour un Union Type ou un type primitif :
type UnionAlias = Car | Bus
type PrimitiveAlias = string
Il est important de noter qu'un Type Alias n'est pas un nouveau type au même titre que les types primitif qu'on retrouve dans de nombreux langages : string, boolean, number ...
mais simplement un nom associé à un type, un alias donc !
Les interfaces sont une solution alternative au Type Aliases pour nommer un type d'objet. On les déclare tel que :
interface Car {
brandName: string
model: string
}
Mais alors, pourquoi ces 2 concepts co-existent ? Et bien car ils ont quelques différences !
Entrons tout de suite dans le vif du sujet !
Il est facile d'étendre une interface pour, par exemple, créer des templates d'objet qui nous rapellent les concepts basiques de Programmation Orientée Objet :
interface Animal {
name: string
}
interface Cat extends Animal {
paws: number
}
const maineCoon: Cat = {
name: 'Pepper',
paws: 4,
}
Pour les types aliases, la syntaxe est un peu différente, mais le résultat est le même.
type Animal = {
name: string
}
type Cat = Animal & {
paws: number
}
const maineCoon: Cat = {
name: 'Pepper',
paws: 4,
}
Egalité, 0️⃣ points !
TIPS : Il est possible d'étendre une interface avec un type et inversement tel que :
type Type1 = {} interface Interface1 extends Type1 {} interface Interface2 {} type Type2 = Type1 & Interface2 {}
On peut ajouter des propriétés à une interface en redéclarant son nom et les nouvelles propriétés.
interface Vehicle {
name: string
}
interface Vehicle {
model: string
}
Notre interface Vehicle
contient maintenant 2 propriétés : name
et model
/!\ Il est important de noter que cette manière d'ajouter des propriétés à un objet ne doit pas être utilisée n'importe comment, au risque de créer du code illisible et non maintenable si on ne retrouve pas toutes les propriétés au même endroit. Cette logique est intéressante si l'on souhaite, par exemple, étendre une interface d'une librairie externe dans un projet avancé.
Avec les Types Aliases, ce n'est pas possible.
type Vehicle = {
name: string
}
type Vehicle = {
//Génère l'erreur : Error: Duplicate identifier 'Vehicle'
type: string
}
Pour ajouter une propriété, on est obligé de déclarer un nouvel Alias (Voir la partie Etendre).
2️⃣ point pour les Interfaces !
La Genericité est un concept incontournable de Typescript. Il a donc naturellement été implémenté dans les 2 concepts que nous mettons en face à face. Son objectif est de rendre des propriétés d'un type ou d'une interface variable lors de sa déclaration.
interface Vehicle<P> {
brand: P
}
const Renault: Vehicle<string> = {
brand: 'renault',
}
type Vehicle<P> = {
brand: P
}
const Renault: Vehicle<string> = {
brand: 'renault',
}
On remarque que les syntaxes des 2 méthodes sont très similaire.
Egalité, 0️⃣ points !
Le fonctionnement des Tuples est un concept intéressant en Typescript qui permet de déclarer un set de valeur avec des types différents. On peut l'utiliser avec des types :
type Cat: [string, number] = ['Pepper', 4]
C'est un peu plus complexe avec les interfaces et moins adapté :
interface Cat {
values: [string, number]
}
1️⃣ point pour les types aliases !
Les Types aliases nous permettent de redéfinir le nom d'un type primitif. Par exemple on peut faire :
type PinCode = number
const pinCode: PinCode = 1234
Cela peut permettre de rendre le code plus compréhensible dans certaines situations.
Ce n'est pas vraiment possible avec les interfaces. On pourrait, au mieux, faire :
interface PinCode {
value: number
}
const pinCode: PinCode = { value: 1234 }
1️⃣ point pour les Type Aliases !
Du côté de la performance, nos 2 challengers réagissent strictement de la même manière à la compilation.
Voici notre fichier typescript :
interface Dog {
name: string
}
type Cat = {
name: string
}
const cat: Cat = { name: 'pepper' }
const dog: Dog = { name: 'scoubidou' }
après transpilation :
var cat = { name: 'pepper' }
var dog = { name: 'scoubidou' }
Les Types Aliases et les interfaces de Typescript sont utilisés uniquement dans la phase de développement et disparaissent lors de la phase de transpilation en Javascript. Il n'y a donc pas de différence de performance entre ces 2 outils.
Encore une égalité, quel match !
Concept de POO
Comme nous l'avons vu plus haut, les interfaces nous permettent de retrouver le concept d'héritage des classes de la Programmation Orientée Objet avec extends
tels que:
interface Animal {...}
interface Dog extends Animal {...}
On retrouve dans l'interface Dog
toutes les propriétés de l'interface Animal
C'est encore un match serré auquel nous avons eu à faire !
Voici le résumé du match
\ | Types | Interfaces |
---|---|---|
Etendre | 0️⃣ | 0️⃣ |
Ajouter une propriété | 0️⃣ | 2️⃣ |
Généricité | 0️⃣ | 0️⃣ |
Tuples | 1️⃣ | 0️⃣ |
Renommer un type primitif | 1️⃣ | 0️⃣ |
Performances | 0️⃣ | 0️⃣ |
Malgrès des différences entre les deux concepts, on retrouve énormément de points communs et de comportements similaires.
Si on se réfère à la documentation officielle de Typescript, celle-ci nous dit que l'on peut utiliser les interfaces par défaut sauf si celles-ci ne nous permettent pas de répondre à nos besoins.
Mon avis
De mon côté, j'ai choisi de suivre le conseil d'utilisation de la documentation officielle car ... c'est la doc officielle ;-) J'utilise donc très largement les interfaces !
Alors, plutôt Interfaces ou Type Aliases ?