Interface TypeScript vs Type : Différences réelles, testées avec le compilateur
Les interfaces et les alias de type se chevauchent tellement que le choix semble souvent arbitraire. Voici ce que chacun peut faire que l’autre ne peut pas faire, confirmé par de véritables erreurs du compilateur, ainsi qu’une règle simple que votre équipe peut suivre.
- Pour les formes d’objet simples, une interface et un alias de type sont presque interchangeables.
- Seul
typepeut nommer des unions, des tuples et d’autres types non-objets. - Seul
interfaceprend en charge la fusion de déclarations, où deux déclarations se combinent. - Une règle commune : des interfaces pour les formes d’objets,
typepour tout le reste, appliquées de manière cohérente.
Peu de questions TypeScript suscitent autant de débats que Interface TypeScript vs type. De nombreux articles répertorient les différences qui étaient importantes dans les anciennes versions, mais qui sont désormais obsolètes. Ainsi, plutôt que de répéter des affirmations, nous les avons testées.
Nous avons écrit un fichier exerçant chaque différence et l’avons vérifié avec TypeScript 5.6.3 en mode strict. Les erreurs du compilateur citées ci-dessous proviennent de cette exécution. Le comportement peut changer entre les versions, alors vérifiez par rapport à la version TypeScript utilisée par votre projet. Si vous êtes nouveau dans le langage, commencez par notre Tutoriel TypeScript pour débutants.
La réponse courte
Un interface décrit la forme d’un objet et peut être étendu ou rouvert. Un alias type donne un nom à n’importe quel type, y compris les objets, les unions, les tuples et les types de fonctions. Là où les deux peuvent fonctionner, la différence réside principalement dans le style.
Où ils sont pareils
Ces deux déclarations décrivent des formes identiques :
interface User {
id: number;
name: string;
email?: string;
}
type UserAlias = {
id: number;
name: string;
email?: string;
};
const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a; // no error
TypeScript utilise le typage structurel, ce qui signifie qu’il compare les formes plutôt que les noms. C’est pourquoi un User peut être attribué à un UserAlias sans aucune plainte. Les deux prennent en charge les propriétés facultatives, les propriétés readonly, les méthodes et les signatures d’index.
Interface vs type côte à côte
| Caractéristique | interface | tapez |
|---|---|---|
| Formes d’objets | Oui | Oui |
Les syndicats, tels que "a" | "b" |
Non | Oui |
| Tuples et primitives | Non | Oui |
| Types mappés et conditionnels | Non | Oui |
| Fusion de déclarations | Oui | Non, c’est une erreur |
| Extension | s'étend |
& intersection |
| Propriétés contradictoires | Erreur lors de la déclaration | Peut tranquillement devenir never |
Utilisé avec implements |
Oui | Oui, s’il s’agit d’un type d’objet |
Ce que seul le type peut faire
De nombreux types utiles ne sont pas du tout des formes d’objet, et seul un alias de type peut les nommer :
type Status = "pending" | "paid" | "shipped"; // union
type Point = [number, number]; // tuple
type Handler = (event: string) => void; // function type
type Keys = keyof User; // computed type
type ReadonlyUser = Readonly<User>; // utility type
Les types d’union font partie des fonctionnalités les plus précieuses de TypeScript, car le compilateur rejette ensuite toute valeur en dehors de la liste :
let s: Status = "paid";
s = "refunded";
// error TS2322: Type '"refunded"' is not assignable to type 'Status'.
Cela détecte les fautes de frappe et les valeurs obsolètes au moment de la compilation, avant qu’elles n’atteignent la production.
Fusion de déclarations
Si vous déclarez deux fois une interface portant le même nom, TypeScript les fusionne en une seule :
interface Settings {
theme: string;
}
interface Settings {
language: string;
}
const settings: Settings = { theme: "dark", language: "en" }; // no error
Essayez la même chose avec un alias de type, et le compilateur refuse :
type Config = { theme: string };
type Config = { language: string };
// error TS2300: Duplicate identifier 'Config'.
La fusion est la façon dont les bibliothèques vous permettent d’étendre leurs types. Par exemple, vous pouvez ajouter une propriété personnalisée à l’interface globale Window ou à l’objet de requête d’un framework, sans modifier la bibliothèque. C’est vraiment utile.
Le revers de la médaille est que la fusion peut se produire par accident. Si deux fichiers d’un projet déclarent une interface portant le même nom dans la même portée, ils se combinent silencieusement, ce qui peut prêter à confusion. Un alias de type fait de cette collision une erreur.
s’étend par rapport aux intersections
Les deux vous permettent de construire sur un type existant :
interface Admin extends User {
permissions: string[];
}
type AdminAlias = UserAlias & { permissions: string[] };
Pour les propriétés compatibles, le résultat est le même. La différence se manifeste par un conflit. Lorsqu’une interface tente de modifier le type d’une propriété de manière incompatible, le compilateur le signale immédiatement :
interface Base { id: number }
interface Broken extends Base { id: string }
// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
// Types of property 'id' are incompatible.
// Type 'string' is not assignable to type 'number'.
Une intersection ne se plaint pas lorsqu’elle est déclarée. Au lieu de cela, la propriété en conflit devient never, un type qu’aucune valeur ne peut avoir. L’erreur n’apparaît que plus tard, lorsque vous essayez de l’utiliser :
type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };
// error TS2322: Type 'number' is not assignable to type 'never'.
Cette erreur ultérieure est plus déroutante, car elle s’éloigne de l’erreur réelle. C’est une raison pratique de préférer extends lors de la création de types d’objets.
Classes et outils
Une classe peut promettre de correspondre à une forme en utilisant implements, et cela fonctionne avec une interface ou un alias de type d’objet :
class Account implements User {
constructor(public id: number, public name: string) {}
}
Ceci compilé sans erreurs. Notez que implements vérifie uniquement que la classe correspond. Il n’ajoute aucune propriété ni code. Une classe ne peut pas implémenter un type d’union, puisqu’il n’existe pas de forme unique à laquelle correspondre.
Types en lecture seule et utilitaires
Les types d’utilitaires tels que Readonly, Partial, Pick et Omit fonctionnent à la fois avec les interfaces et les alias. Le résultat est un nouveau type, généralement nommé avec un alias de type :
const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";
// error TS2540: Cannot assign to 'name' because it is a read-only property.
N’oubliez pas que readonly est uniquement une vérification au moment de la compilation. Au moment de l’exécution, l’objet est du JavaScript ordinaire et il peut toujours être modifié par du code qui contourne les types.
Astuce : Les types disparaissent lorsque TypeScript est compilé en JavaScript. Les données arrivant de l’extérieur, telles qu’une réponse API, ne sont pas vérifiées au moment de l’exécution simplement parce qu’elles ont un type. Validez-le, par exemple avec une bibliothèque de schémas. Notre Tutoriel Fetch API couvre la gestion des réponses API en toute sécurité.
Performances du compilateur
Les propres conseils de performance de l’équipe TypeScript suggèrent de préférer les interfaces qui utilisent extends aux grandes intersections, car le compilateur peut mettre en cache les relations d’interface plus efficacement. Dans les petits et moyens projets, il est peu probable que vous remarquiez une différence. Cela peut avoir de l’importance dans les bases de code très volumineuses avec des types profondément composés. Nous ne l’avons pas mesuré nous-mêmes.
Lequel devriez-vous utiliser ?
Une règle largement utilisée, conformément aux conseils du manuel TypeScript, est la suivante :
- Utilisez
interfacepour les formes d’objets, en particulier les API publiques et tout ce que les classes implémentent ou que d’autres peuvent étendre. - Utilisez
typepour les unions, les tuples, les types de fonctions et les types mappés ou calculés.
| Vous définissez | Utiliser |
|---|---|
| Un modèle de données, tel que Utilisateur ou Commande | interface |
| Un ensemble de valeurs autorisées | type syndicat |
| Une paire ou un triple de longueur fixe | type tuple |
| Une signature de rappel | tapez |
| Extension des types d’une bibliothèque | interface, en fusionnant |
| Une variante d’un type existant | type avec un type d’utilitaire |
Certaines équipes utilisent simplement type partout, et cela fonctionne également bien. Ce qui compte le plus, c’est la cohérence, alors choisissez une convention, écrivez-la dans votre guide de style et laissez un linter l’appliquer. La règle ESLint consistent-type-definitions de typescript-eslint peut le faire.
Erreurs courantes
- Débat sans fin. Les deux conviennent parfaitement aux objets, alors définissez la règle une fois et passez à autre chose.
- Fusion accidentelle. La réutilisation d’un nom d’interface dans la même portée les combine silencieusement.
- Intersections conflictuelles. Ils créent plus tard
nevertypes avec des erreurs déroutantes. - Types de confiance au moment de l’exécution. Ils sont effacés, donc validez les données externes.
- Utiliser
anypour faire taire les erreurs. Cela désactive la vérification. Préférezunknownet réduisez-le.
Basculer entre eux dans une base de code existante
Changer un interface en type, ou l’inverse, est généralement sans danger pour les formes d’objet simples, car TypeScript compare les types par leur structure, et non par leurs noms. Quelques éléments doivent encore être vérifiés avant de changer :
- La conversion d’une interface en alias de type supprime la fusion des déclarations, de sorte que toute deuxième déclaration portant le même nom devient une erreur d’identifiant en double.
- La conversion d’un alias de type en interface ne fonctionne que pour les formes d’objet. Les unions, les tuples et les types mappés doivent rester sous forme d’alias de type.
- Les messages d’erreur et les survols de l’éditeur peuvent afficher le type différemment, ce qui peut surprendre les coéquipiers mais ne change pas le comportement.
Effectuez la modification en une seule demande d’extraction ciblée, exécutez le compilateur en mode strict et votre suite de tests, et évitez de la mélanger avec le travail sur les fonctionnalités afin que les réviseurs puissent voir que rien d’autre n’a changé.
Pour conclure
La question interface vs type a une réponse calme : ils se chevauchent pour les objets, chacun a quelques capacités uniques et la cohérence compte plus que le choix. Utilisez des interfaces pour les formes et les types pour tout le reste, à moins que votre équipe ne préfère autrement. Pour voir les différences vous-même, copiez les exemples ci-dessus dans un fichier et exécutez tsc --noEmit --strict dessus.
Questions fréquentes
Quelle est la différence entre l’interface et le type dans TypeScript ?
Les deux peuvent décrire des formes d’objets. Seul le type peut nommer les unions, les tuples et les types calculés, tandis que seule l’interface prend en charge la fusion des déclarations.
Dois-je utiliser une interface ou un type pour les accessoires React ?
L’un ou l’autre fonctionne bien. De nombreuses équipes utilisent des interfaces pour les accessoires, tandis que d’autres préfèrent les caractères pour des raisons de cohérence avec les unions. Choisissez-en un et appliquez-le tout au long du projet.
Un type peut-il étendre une interface ?
Oui, en utilisant une intersection telle que type Admin = User & { permissions: string[] }. Une interface peut également étendre un alias de type d’objet.
L’interface est-elle plus rapide que la saisie ?
Les conseils en matière de performances de TypeScript privilégient l’extension de l’interface sur de grandes intersections dans de grandes bases de code. Dans les projets typiques, il est peu probable que la différence soit perceptible.
Qu’est-ce que la fusion de déclarations ?
C’est lorsque deux interfaces portant le même nom dans la même portée se combinent en une seule. Les alias de type ne peuvent pas fusionner et les noms en double provoquent une erreur.
Continuez à apprendre
Besoin d’aide pour les tests, WordPress ou le référencement ?
Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.
Plus de guides sur tests, IA et développement moderne
Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.
Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire
Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …
Lire l'article →DevOpsVariables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués
Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…
Lire l'article →Développement logicielLimitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429
Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…
Lire l'article →Automatisation des testsSe moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests
Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…
Lire l'article →Tests logicielsCouverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %
Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…
Lire l'article →Bases de données et SQLDELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser
DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …
Lire l'article →Bases de données et SQLVues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples
Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …
Lire l'article →Développement logicielInterface TypeScript vs Type : différences réelles, testées avec le compilateur
Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…
Lire l'article →Développement logicielPython *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs
Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…
Lire l'article →Développement logicielFonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser
Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…
Lire l'article →Intelligence artificielleQue sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter
Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…
Lire l'article →DevOpsSurveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces
Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…
Lire l'article →DevOpsVolumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière
Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…
Lire l'article →Automatisation des testsTDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser
TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…
Lire l'article →Développement logicielGit merge vs rebase : différences, exemples et quand utiliser chacun
Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.
Lire l'article →Développement logicielList comprehension Python : syntaxe, exemples et cas à éviter
La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…
Lire l'article →Bases de données et SQLJointures SQL expliquées avec exemples (Inner, Left, Right, Full)
Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…
Lire l'article →