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 :
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 }
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 :
Inconvénients :
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 :
Inconvénients :