ProgrammationDéveloppeur Fullstack

Décrivez le mécanisme des types d'intersection (Intersection Types) dans TypeScript. Comment peuvent-ils être utilisés pour implémenter des types combinés, quelle est la différence avec les types union, et quelles sont les principales subtilités concernant l'héritage et la compatibilité des propriétés ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Les types d'intersection (Intersection Types) dans TypeScript permettent de créer des types composites qui combinent les propriétés et méthodes de tous les types d'origine. C'est un outil puissant pour construire des structures de données flexibles et extensibles sans héritage excessif de classes. La construction est réalisée à l'aide de l'opérateur & entre les types.

Historique de la question

TypeScript prend en charge les types union (|) depuis les premières versions pour exprimer “ou”, mais il arrive souvent qu'il soit nécessaire de décrire un objet avec de nombreuses propriétés provenant de différentes interfaces ou types indépendants. C'est alors que l'intersection (&) est utilisée - l'objet doit satisfaire toutes les interfaces pour toutes les propriétés.

Problème

Une des principales difficultés est le conflit des noms de propriétés identiques dans les types d'intersection, la différence dans leur typage, ainsi que la validité du type combiné final si des classes avec des champs privés ou protégés sont fusionnées. Il y a aussi souvent une confusion entre l'intersection des types et la union (union types), ce qui conduit à des erreurs de compilation peu évidentes ou à des problèmes lors de la manipulation de l'objet.

Solution

L'intersection des types agrège toutes les propriétés des types réunis, et pour chaque propriété, une correspondance avec les deux types est requise si le nom coïncide. Cela s'applique aussi bien aux interfaces qu'aux alias de type.

interface A { foo: string; } interface B { bar: number; } type AB = A & B; const item: AB = { foo: "hello", bar: 123 }; // Correct

Si des propriétés avec le même nom sont en intersection, le type doit correspondre, sinon une erreur se produira :

interface X { val: string; } interface Y { val: number; } // type Z = X & Y; // Erreur : incompatibilité val

Caractéristiques clés :

  • L'opérateur & combine toutes les propriétés des deux types (interfaces et alias de type).
  • Si une propriété existe dans les deux types, le type final est leur intersection (il doit correspondre aux deux).
  • Utilisé pour combiner la fonctionnalité de manière flexible sans créer de nouvelles classes ou hiérarchies.

Questions pièges.

Que se passe-t-il si les types se croisent et que certaines propriétés ont des types incompatibles ? (Par exemple, une propriété est string, l'autre number)

Il y aura une erreur de compilation, car la propriété ne peut être à la fois string et number en même temps.

Peut-on croiser des classes avec des propriétés privées ou protégées ?

Oui, mais si de tels champs existent avec des noms identiques et différents niveaux d'accès, le résultat sera invalide, et TypeScript renverra une erreur.

En quoi le type d'intersection diffère-t-il du type d'union (|) ?

L'intersection (A & B) exige que l'objet soit des deux types en même temps, tandis que l'union (A | B) exige qu'il soit au moins l'un ou l'autre. Par exemple :

type U = A | B; // Peut être foo ou bar ou les deux type I = A & B; // Doit avoir les deux propriétés

Erreurs typiques et anti-patterns

  • L'intersection de types incompatibles ou des conflits de propriétés avec des types différents entraîneront des erreurs.
  • L'abus d'intersection pour contourner la typage stricte rend le code confus.

Exemple de la vie réelle

Cas négatif

Un développeur crée un type en croisant plusieurs interfaces incohérentes ; les propriétés entrent en conflit, des erreurs de typage difficiles à déboguer apparaissent.

Avantages : Possibilité de combiner rapidement les capacités de plusieurs types.

Inconvénients : Le code ne compile pas ou contient des bogues implicites, le débogage est difficile.

Cas positif

Séparer les fonctions en interfaces indépendantes et combiner soigneusement celles-ci via le type d'intersection pour former des objets composites finaux.

Avantages : Scalabilité, facilité de test et d'extension, contrat strict.

Inconvénients : Travail supplémentaire pour concevoir les interfaces et les rendre compatibles.