ProgrammationDéveloppeur Frontend

Comment fonctionne le mécanisme d'extension des types d'objets en TypeScript via l'opérateur Spread et comment cela influence-t-il la typage ? Quelles pièges typiques et situations atypiques peuvent survenir lors de l'extension de structures complexes ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En JavaScript, l'opérateur Spread (...) a été introduit pour faciliter la copie et l'extension des objets, et il est couramment utilisé avec les tableaux depuis longtemps, et à partir de ES2018 — aussi avec des objets. En TypeScript, Spread est devenu non seulement une amélioration syntaxique, mais aussi un outil pour une typage précise lors de manipulations avec des objets.

Historiquement, en JavaScript, le clonage ou l'extension d'un objet se faisait via Object.assign, mais cette approche pouvait facilement conduire à une perte de sécurité de typage et à des intersections de clés dangereuses si la structure de l'objet était complexe.

Le problème réside dans le fait qu'en utilisant Spread pour combiner / étendre des objets, TypeScript déduit un nouveau type basé sur les structures d'entrée, en traitant les conflits de clés possibles (« le dernier l'emporte »), mais ce n'est pas toujours ce que le développeur avait en tête. Il est particulièrement important de prêter attention aux intersections avec des champs optionnels, en lecture seule et à la présence de propriétés privées dans les classes.

Solution : utiliser Spread, permettant au compilateur TypeScript de déduire le résultat automatiquement. Pour des cas complexes, il est important de limiter explicitement le type des structures d'entrée et de suivre attentivement les changements de structures de types.

Exemple de code :

interface User { name: string; age: number; } interface Extra { isAdmin?: boolean; readonly city: string; } const base: User = { name: 'Ivan', age: 28 }; const extended: User & Extra = { ...base, city: 'Moscou', isAdmin: true };

Caractéristiques clés :

  • Spread fusionne les clés, la dernière clé rencontrée définit la valeur et le type.
  • Les champs readonly et optionnels sont transférés, mais peuvent être écrasés si les types d'origine sont en conflit.
  • Lors de l'extension des classes, les propriétés privées ne sont pas copiées, ce qui peut entraîner des erreurs.

Questions pièges.

Peut-on obtenir une "copie profonde" d'un objet en TypeScript à l'aide d'un héritage Spread ?

Réponse : Non. Spread ne fait qu'une copie superficielle (shallow copy), les objets imbriqués restent par référence.

const original = { user: { name: 'Anna' } }; const cloned = { ...original }; cloned.user.name = 'Maria'; // original.user.name changera aussi

Le type sera-t-il restreint ou élargi lors de l'utilisation de Spread sur des objets avec des clés qui se chevauchent, et comment cela affecte-t-il l'annotation de la variable ?

Réponse : Lors de l'utilisation de Spread, le type de la variable est élargi, et en cas de chevauchement de clés, les propriétés de droite l'emportent ; si une annotation de type explicite est présente, une erreur peut survenir si les types sont incompatibles.

const a = { id: 4, value: "abc" }; const b = { value: 123 }; const c: { id: number; value: number } = { ...a, ...b }; // ok

Spread peut-il être appliqué aux classes et que se passe-t-il avec les propriétés privées ?

Réponse : Spread ne s'applique qu'aux propriétés publiques de la classe. Les champs privés (private/#) et protégés protected ne figureront pas dans l'objet résultant.

class Person { private id = 77; name = "Bob"; } const p = new Person(); const spreaded = { ...p }; // spreaded: { name: string }

Erreurs de typage et anti-patterns

  • Essayer d'utiliser Spread pour une copie profonde d'objets imbriqués.
  • Oublier les dangers des conflits de clés et des changements de types.
  • Utiliser Spread avec des instances de classes en s'attendant à copier des propriétés privées.
  • Appliquer Spread à des tableaux avec des types hétérogènes, perdant la sécurité des types.

Exemple de la vie réelle

Cas négatif

Un développeur utilise Spread pour fusionner les valeurs d'un objet settings pour étendre les options utilisateur :

const common = { theme: 'light', notifications: true }; const user = { notifications: false, signature: 'Sasha' }; const merged = { ...common, ...user };

Il s'attend à ce que merged soit cohérent au niveau du type, mais fait accidentellement une erreur avec le type du champ signature, en oubliant que Spread se contente de copier les clés, sans valider leurs valeurs.

Avantages :

  • Rapide, pratique pour fusionner des objets

Inconvénients :

  • Écrasement des valeurs importantes, perte de certaines propriétés obligatoires, erreurs invisibles lors de l'ajout de nouvelles clés.

Cas positif

Pour fusionner des configurations, Spread est utilisé uniquement après validation et avec annotation de type de retour :

interface Settings { theme: "light" | "dark"; notifications: boolean; signature?: string; } function getSettings(common: Settings, specific: Partial<Settings>): Settings { return { ...common, ...specific }; }

Avantages :

  • Contrôle des types, extension transparente,
  • Sécurité de validation,
  • Visibilité de la structure de l'objet.

Inconvénients :

  • Nécessite plus de code,
  • Besoin de maintenir la pertinence de l'interface.