Destructuring JavaScript ES6 : tableaux, objets, valeurs defaut, renommage, rest pattern, typage TypeScript, parametres React/Vue et 10 patterns reels.
Destructuring de tableau — syntaxe et variantes
Le destructuring de tableau extrait les valeurs par position (contrairement aux objets qui extraient par nom). La syntaxe utilise des crochets [] à gauche de l'assignation.
const rgb = [255, 128, 0];
// Extraction positionnelle
const [red, green, blue] = rgb;
console.log(red, green, blue); // 255 128 0
// Ignorer des éléments avec des virgules vides
const [, , alpha] = [1, 2, 0.8];
console.log(alpha); // 0.8
// Rest operator — capture le reste dans un nouveau tableau
const [first, second, ...remaining] = [10, 20, 30, 40, 50];
console.log(first); // 10
console.log(second); // 20
console.log(remaining); // [30, 40, 50]
Swap de variables sans variable temporaire
let a = 1;
let b = 2;
// Avant ES6 : nécessitait une variable temporaire
// let temp = a; a = b; b = temp;
// Avec destructuring : élégant et lisible
[a, b] = [b, a];
console.log(a, b); // 2 1
// Cas pratique : algorithmes de tri (bubble sort, quicksort)
function bubbleSort(arr: number[]): number[] {
const result = [...arr]; // copie immutable
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result.length - i - 1; j++) {
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]]; // swap
}
}
}
return result;
}
Destructuring avec les itérables (Map, Set, générateurs)
// Fonctionne avec tout itérable, pas seulement les tableaux
const coordinates = new Map([['x', 10], ['y', 20], ['z', 5]]);
const [[, x], [, y]] = coordinates; // x=10, y=20
// Avec un Set
const [first, ...others] = new Set([1, 2, 3, 4, 5]);
console.log(first); // 1
console.log(others); // [2, 3, 4, 5] (array, pas Set)
// Avec une string (chaque caractère est un élément)
const [h, e, l1, l2, o] = 'Hello';
console.log(h); // 'H'
Destructuring d'objet — extraction et renommage
Le destructuring d'objet extrait les valeurs par nom de propriété. La syntaxe utilise des accolades {}. L'ordre n'a pas d'importance — seul le nom de la clé compte.
const user = {
id: 42,
firstName: 'Alice',
lastName: 'Martin',
email: 'alice@example.com',
role: 'admin',
createdAt: new Date('2023-06-15'),
};
// Extraction simple — les variables héritent du nom de la propriété
const { id, firstName, role } = user;
console.log(id, firstName, role); // 42 'Alice' 'admin'
// Renommage : propriété: nouvelleVariable
// Utile quand le nom de propriété entre en conflit avec une variable existante
const { firstName: fName, lastName: lName } = user;
console.log(fName, lName); // 'Alice' 'Martin'
// Renommage + valeur par défaut combinés
const { role: userRole = 'viewer', theme: userTheme = 'light' } = user;
// userRole = 'admin' (valeur de user.role)
// userTheme = 'light' (défaut car user.theme est undefined)
Rest operator sur les objets — cloner sans certaines clés
const userWithPassword = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
passwordHash: 'bcrypt$2a$12$...',
sessionToken: 'eyJhbGci...',
};
// Extraire les champs sensibles et garder le reste
const { passwordHash, sessionToken, ...safeUser } = userWithPassword;
// safeUser = { id: 1, name: 'Alice', email: 'alice@example.com' }
// passwordHash et sessionToken sont disponibles mais pas dans safeUser
// Pattern courant : normaliser les données de l'API
const { user_id: userId, first_name: firstName, ...extras } = apiResponse;
// Convertit snake_case → camelCase proprement
Valeurs par défaut — undefined vs null
La valeur par défaut est appliquée uniquement si la valeur est undefined. Une valeur null explicite ne déclenche PAS la valeur par défaut — c'est un piège très courant.
// Comportement exact des valeurs par défaut
const config = {
timeout: undefined, // déclenche la valeur par défaut
retries: null, // NE déclenche PAS la valeur par défaut
debug: false, // NE déclenche PAS (false est une valeur définie)
maxSize: 0, // NE déclenche PAS (0 est une valeur définie)
};
const {
timeout = 5000, // → 5000 (undefined déclenche le défaut)
retries = 3, // → null (null ne déclenche PAS le défaut!)
debug = true, // → false (false est défini, pas de défaut)
maxSize = 100, // → 0 (0 est défini, pas de défaut)
logLevel = 'info', // → 'info' (propriété absente = undefined)
} = config;
console.log(timeout, retries, debug, maxSize, logLevel);
// 5000, null, false, 0, 'info'
// Gestion correcte quand null ET undefined doivent utiliser le défaut
function createConfig(overrides: Partial<AppConfig>): AppConfig {
const {
port,
host,
timeout,
} = overrides;
return {
port: port ?? 3000, // ?? : null ET undefined → défaut
host: host ?? 'localhost',
timeout: timeout ?? 5000,
};
}
// VS avec la valeur par défaut du destructuring (undefined seulement)
const { port = 3000, host = 'localhost' } = overrides;
// null passe à travers — comportement différent selon le besoin
Destructuring imbriqué — profondeur et limites
Le destructuring imbriqué permet d'extraire des valeurs dans des structures profondes. Mais au-delà de 2 niveaux, la lisibilité se dégrade rapidement.
// Niveau 1 : lisible
const { address: { city, zip } } = user;
// Niveau 2 : acceptable mais attention
const { billing: { address: { street, city: billingCity } } } = order;
// Niveau 3+ : illisible → mieux vaut des variables intermédiaires
// Eviter :
const { a: { b: { c: { d } } } } = deepObject; // trop profond
// Préférer :
const { b } = deepObject.a;
const { d } = b.c;
// Exemple réel : réponse d'API imbriquée
interface ApiOrder {
data: {
order: {
id: number;
customer: { name: string; email: string };
total: number;
};
};
meta: { requestId: string };
}
function processApiResponse(response: ApiOrder) {
// Destructuring imbriqué pratique (2 niveaux)
const {
data: {
order: { id, customer, total }
},
meta: { requestId }
} = response;
console.log(`Order #${id} — ${customer.name} — ${total}€`);
console.log(`Request: ${requestId}`);
}
Dans les paramètres de fonction
Le destructuring dans les paramètres de fonction rend les APIs plus lisibles — le nom des paramètres est toujours visible à l'appel, contrairement aux paramètres positionnels.
// AVANT : paramètres positionnels — ordre critique, noms invisibles à l'appel
function createUser(name: string, email: string, role: string, active: boolean): void { /* */ }
createUser('Alice', 'alice@ex.com', 'admin', true); // que signifie true ?
// APRÈS : destructuring — ordre libre, self-documenting
function createUser({
name,
email,
role = 'user', // valeur par défaut
active = true,
sendWelcomeEmail = false,
}: {
name: string;
email: string;
role?: 'admin' | 'user' | 'moderator';
active?: boolean;
sendWelcomeEmail?: boolean;
}): void { /* */ }
createUser({ name: 'Alice', email: 'alice@ex.com', role: 'admin' });
// Lisible, ordre libre, valeurs par défaut transparentes
// Destructuring dans les callbacks Angular / RxJS
import { map } from 'rxjs/operators';
// Sans destructuring
this.http.get<Order>('/api/orders/1').pipe(
map(order => ({
id: order.id,
total: order.total,
customerName: order.customer.name,
}))
);
// Avec destructuring dans le callback map
this.http.get<Order>('/api/orders/1').pipe(
map(({ id, total, customer: { name: customerName } }) => ({
id,
total,
customerName,
}))
);
Opérateur rest et spread combinés
Le rest ... dans le destructuring capture le reste des éléments. Le spread ... les disperse. Combinés, ils permettent des transformations immuables élégantes.
// Pattern immuable : mettre à jour une propriété sans muter l'objet
const user = { id: 1, name: 'Alice', email: 'alice@ex.com', role: 'user' };
// Met à jour role en créant un nouvel objet — immutabilité garantie
const updatedUser = { ...user, role: 'admin' };
// { id: 1, name: 'Alice', email: 'alice@ex.com', role: 'admin' }
// user original inchangé
// Supprimer une propriété immuablement
const { role, ...userWithoutRole } = user;
// userWithoutRole = { id: 1, name: 'Alice', email: 'alice@ex.com' }
// Fusionner des objets avec précédence (dernier gagne)
const defaults = { timeout: 5000, retries: 3, debug: false };
const overrides = { debug: true, maxConnections: 10 };
const config = { ...defaults, ...overrides };
// { timeout: 5000, retries: 3, debug: true, maxConnections: 10 }
// debug override : overrides a la précédence sur defaults
// Merge de tableaux avec déduplication (Set trick)
const arr1 = [1, 2, 3, 4];
const arr2 = [3, 4, 5, 6];
const merged = [...new Set([...arr1, ...arr2])];
// [1, 2, 3, 4, 5, 6] — sans doublons
Dans les boucles et les imports
// Destructuring dans for...of — très courant avec les collections de données
const products = [
{ id: 1, name: 'Laptop', price: 999, category: 'tech' },
{ id: 2, name: 'Desk', price: 350, category: 'furniture' },
{ id: 3, name: 'Phone', price: 699, category: 'tech' },
];
for (const { id, name, price } of products) {
console.log(`${id}: ${name} — ${price}€`);
}
// entries() pour avoir l'index ET la valeur
for (const [index, { name, price }] of products.entries()) {
console.log(`[${index}] ${name}: ${price}€`);
}
// Object.entries() pour parcourir les paires clé/valeur
const config = { host: 'localhost', port: 3000, debug: true };
for (const [key, value] of Object.entries(config)) {
console.log(`${key} = ${value}`);
}
// Destructuring dans les imports ES modules
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { signal, computed, effect } from '@angular/core';
// Import avec renommage (évite les conflits de noms)
import { map as rxMap, filter as rxFilter } from 'rxjs/operators';
// Import tout + accès par nom
import * as _ from 'lodash';
const { groupBy, sortBy, uniqBy } = _; // destructuring depuis l'import namespace
Typage en TypeScript
// TypeScript infère les types depuis la source
const point = { x: 10, y: 20 };
const { x, y } = point;
// x: number, y: number — inféré automatiquement
// Destructuring avec type annoté explicitement
function parseConfig({ host, port = 3000, ssl = false }: {
host: string;
port?: number;
ssl?: boolean;
}): string {
return `${ssl ? 'https' : 'http'}://${host}:${port}`;
}
// Destructuring d'un type union — TypeScript force la vérification
type Event =
| { type: 'click'; x: number; y: number }
| { type: 'keydown'; key: string };
function handle(event: Event): void {
if (event.type === 'click') {
const { x, y } = event; // TypeScript sait que x, y existent ici
console.log(`Clicked at (${x}, ${y})`);
} else {
const { key } = event; // TypeScript sait que key existe ici
console.log(`Key pressed: ${key}`);
}
}
Pièges courants à éviter
// PIÈGE 1 : destructuring null ou undefined → ReferenceError
const user = null;
const { name } = user; // TypeError: Cannot destructure property 'name' of null
// Protection :
const { name } = user ?? {}; // fallback vers objet vide
const { name } = user || {}; // même effet (mais || court-circuite sur tout falsy)
// PIÈGE 2 : shadowing de variable — le renommage overwrite
const id = 99; // variable existante
const { id } = user; // SyntaxError: Identifier 'id' has already been declared
// Solution : renommer
const { id: userId } = user; // userId = user.id, id reste 99
// PIÈGE 3 : valeur par défaut sur null (null ≠ undefined)
const settings = { timeout: null };
const { timeout = 5000 } = settings;
console.log(timeout); // null — pas 5000!
// null ne déclenche pas la valeur par défaut — seul undefined le fait
// Solution propre avec ??=
const resolvedTimeout = settings.timeout ?? 5000; // 5000 car null
- Tableau : extraction par position avec
[] - Objet : extraction par nom avec
{}, renommage avecprop: alias - Valeur par défaut : uniquement sur
undefined, pas surnull - Rest
...: capture le reste, toujours en dernière position - Imbriqué : 2 niveaux max pour la lisibilité
Destructuring dans les paramètres de fonction
C'est l'usage le plus rentable du destructuring : déclarer les propriétés attendues directement dans la signature de la fonction. Le code devient auto-documenté, l'autocomplete IDE plus précis, et le typage TypeScript naturel.
// ❌ Sans destructuring — verbeux, propriétés masquées
function envoyerEmail(options) {
const to = options.to;
const subject = options.subject;
const body = options.body ?? '';
const cc = options.cc ?? [];
// ... appel API
}
// ✅ Avec destructuring — signature explicite
function envoyerEmail({ to, subject, body = '', cc = [] }) {
// to, subject, body, cc directement disponibles
}
// ✅ TypeScript — typage en une ligne
type EmailOptions = {
to: string;
subject: string;
body?: string;
cc?: string[];
};
function envoyerEmail({ to, subject, body = '', cc = [] }: EmailOptions) {
// typage complet, valeurs par défaut intégrées
}
Pattern React et Vue — destructurer les props
// React — extraire les props en une ligne
function UserCard({ name, email, avatarUrl, isAdmin = false }) {
return (
<article>
<img src={avatarUrl} alt={name} />
<h3>{name} {isAdmin && '⭐'}</h3>
<p>{email}</p>
</article>
);
}
// Vue 3 avec script setup
<script setup lang="ts">
const { name, email, isAdmin = false } = defineProps<{
name: string;
email: string;
isAdmin?: boolean;
}>();
</script>
Destructurer + rest pour transmettre les props restantes
// Pattern courant en React — wrapper de Button qui passe tout au native
function PrimaryButton({ label, icon, ...rest }) {
return (
<button {...rest} className="btn-primary">
{icon && <span>{icon}</span>}
{label}
</button>
);
}
// Usage
<PrimaryButton
label="Sauvegarder"
icon="💾"
onClick={handleSave}
disabled={isLoading}
type="submit"
data-testid="save-button"
/>
// onClick, disabled, type, data-testid sont automatiquement transmis
Destructuring + TypeScript : 5 patterns essentiels
1. Typer le destructuring d'objet
// Type inline — simple mais peu réutilisable
function showUser({ name, age }: { name: string; age: number }) {}
// Type nommé — préféré pour la réutilisation
interface UserBasic { name: string; age: number; }
function showUser({ name, age }: UserBasic) {}
// Avec valeurs par défaut typées
function showUser({ name, age = 18 }: UserBasic) {} // age devient required
2. Renommer en typant
// On peut renommer ET typer en même temps
const apiResponse: { id: number; name: string } = { id: 42, name: 'Alice' };
const { id: userId, name: userName }: { id: number; name: string } = apiResponse;
3. Destructurer un tuple typé
// TypeScript distingue les tuples des tableaux
type Coordinates = [number, number, number]; // tuple à 3 éléments
const point: Coordinates = [10, 20, 30];
const [x, y, z] = point; // x, y, z typés number
// Avec named tuples (TS 4.0+) — auto-documenté
type Range = [start: number, end: number, step?: number];
const range: Range = [0, 100, 5];
const [start, end, step = 1] = range;
4. Destructurer le retour d'une fonction
// Pattern courant : hook qui retourne un tuple ou un objet
function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
return [count, setCount, () => setCount(0)] as const; // const = readonly tuple
}
const [count, setCount, reset] = useCounter(10);
// count: number, setCount: function, reset: function — tous typés
5. Destructurer une union typée — TypeScript narrow
type Result =
| { ok: true; value: string }
| { ok: false; error: string };
function handle(result: Result) {
// Destructurer après narrowing
if (result.ok) {
const { value } = result; // value: string
console.log(value);
} else {
const { error } = result; // error: string
console.error(error);
}
}
10 patterns réels du destructuring en production
1. Swap de variables — sans variable temporaire
let a = 1, b = 2;
[a, b] = [b, a]; // swap atomique
// a = 2, b = 1
2. Extraire plusieurs valeurs d'une regex
const url = 'https://example.com/users/42';
const [, host, path] = url.match(/^https?:\/\/([^\/]+)(\/.*)$/) ?? [];
// host = 'example.com', path = '/users/42'
3. Configuration optionnelle avec valeurs par défaut
function createClient({
baseUrl = 'https://api.example.com',
timeout = 5000,
retries = 3,
headers = {},
} = {}) {
// Utilisable sans aucun argument : createClient()
// Ou avec une config partielle : createClient({ timeout: 10000 })
}
4. Aplatir une structure imbriquée
const response = {
data: { user: { profile: { name: 'Alice' } } },
meta: { timestamp: Date.now() },
};
const { data: { user: { profile: { name } } }, meta: { timestamp } } = response;
// name = 'Alice', timestamp = ...
5. Omettre des propriétés (rest pattern)
// Retirer le mot de passe avant d'envoyer un user au front
function sanitize(user) {
const { password, ...safe } = user;
return safe; // tout sauf password
}
6. Convertir un objet en arguments positionnels
const config = { width: 1920, height: 1080, fps: 60 };
const { width, height, fps } = config;
// passage à une API qui veut des arguments séparés
recorder.start(width, height, fps);
7. Iteration avec destructuring d'index
const items = ['a', 'b', 'c'];
for (const [index, value] of items.entries()) {
console.log(index, value); // 0 'a', 1 'b', 2 'c'
}
// Object.entries pour les objets
const user = { name: 'Alice', age: 30 };
for (const [key, val] of Object.entries(user)) {
console.log(`${key}: ${val}`);
}
8. Destructurer un Map
const cache = new Map();
cache.set('user-1', { name: 'Alice' });
cache.set('user-2', { name: 'Bob' });
for (const [id, user] of cache) {
console.log(`${id} → ${user.name}`);
}
9. Promise.all + destructuring parallèle
// Trois appels en parallèle, extraction propre des résultats
const [user, orders, notifications] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/orders').then(r => r.json()),
fetch('/api/notifications').then(r => r.json()),
]);
10. Destructurer dans un callback de Array.map
const users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
];
// Extraction directe dans le callback
const names = users.map(({ name }) => name); // ['Alice', 'Bob']
const adminNames = users
.filter(({ role }) => role === 'admin')
.map(({ name }) => name);
Performance et bonnes pratiques
Le destructuring est devenu une syntaxe si naturelle qu'on l'oublie souvent dans
les bilans techniques. Pourtant, sur une codebase moyenne de 50 000 lignes, on
trouve typiquement 2 000 à 5 000 usages du destructuring — bien plus que de
for classiques. C'est l'une des features les plus omniprésentes du
JavaScript moderne, comparable aux template literals et aux arrow functions par
son taux d'adoption. Connaître ses pièges et ses variantes avancées paie chaque
jour de développement.
Compatibilité navigateurs et transpilation
Le destructuring est supporté nativement dans tous les navigateurs modernes depuis 2017 (Chrome 49, Firefox 41, Safari 10, Edge 13). Aucune transpilation Babel n'est nécessaire si votre target est ES2017 ou plus récent — ce qui est le cas par défaut sur tout projet Vite, Next.js ou Angular 17+ moderne. Sur des projets legacy qui ciblent IE11, Babel transpile automatiquement avec un léger overhead de taille de bundle, mais ces cas sont devenus rares en 2026 avec l'abandon officiel d'IE par Microsoft.
Lisibilité — l'argument principal
Au-delà des aspects techniques, l'argument décisif pour adopter le destructuring partout est la lisibilité. Un développeur qui ouvre votre fichier voit immédiatement quelles propriétés sont consommées par chaque fonction, sans avoir à scanner le corps. La signature devient une mini-documentation auto-générée. Sur des composants React ou Angular qui reçoivent une dizaine de props, cette clarté multiplie la vitesse de lecture du code. C'est l'une des features les plus appréciées par les développeurs en revue de code.
Coût d'exécution — quasi nul
Le destructuring est compilé en JavaScript moderne (ES2015+) sans transformation lourde — V8 et les moteurs modernes l'optimisent parfaitement. La performance est identique à un accès propriété par propriété. Aucune raison de l'éviter pour des raisons de vitesse, même dans des boucles serrées.
Coût de lisibilité — variable selon la profondeur
Le seul piège est la profondeur. Un destructuring sur 3+ niveaux d'imbrication devient illisible et fragile (un nœud manquant casse tout). Au-delà de 2 niveaux, préférez deux étapes :
// ❌ Difficile à lire — 4 niveaux
const { data: { user: { profile: { contact: { email } } } } } = response;
// ✅ En 2 étapes
const profile = response.data?.user?.profile;
const email = profile?.contact?.email;
À faire
- Destructurer les paramètres de fonction pour signatures explicites.
- Combiner avec le rest pattern (
...rest) pour transmettre des props. - Utiliser des valeurs par défaut dans le destructuring — moins de
?? defautdans le corps. - Typer en TypeScript avec interface/type plutôt qu'inline pour réutilisabilité.
- Renommer (
{ prop: alias }) quand le nom collisionne ou est ambigu.
À éviter
- Destructurer sur 3+ niveaux d'imbrication — utilisez des étapes intermédiaires.
- Compter sur les valeurs par défaut pour
null— elles ne se déclenchent que surundefined. - Mélanger destructuring et chaînage optionnel sans précaution (
{ a } = obj?.bcrash siobj?.bestnull). - Renommer toutes les variables — perd l'avantage d'auto-documentation.
Destructuring dans les imports ES modules
La syntaxe import { x, y } from 'module' ressemble au destructuring d'objet,
mais c'est une syntaxe distincte de l'import nommé. La distinction est
importante : on ne peut pas y appliquer toutes les variantes du destructuring (pas de
rest pattern, pas de destructuring profond).
// Import nommé — ressemble au destructuring mais ne l'est pas
import { useState, useEffect } from 'react';
// Renommage à l'import — syntaxe "as" au lieu de ":"
import { useState as useReactState } from 'react';
// Import par défaut + nommés combinés
import React, { useState, useEffect } from 'react';
// Import tout dans un namespace
import * as ReactDOM from 'react-dom';
const { render, hydrate } = ReactDOM; // ICI destructuring classique
Cette distinction est source de confusion fréquente pour les développeurs qui
débutent en TypeScript ou en bundlers modernes. Retenez la règle : la syntaxe
import { x } from 'module' appartient au système de modules ES, elle
est résolue à la compilation, et son comportement est statique. Le destructuring
JavaScript se fait toujours à l'exécution, sur une valeur déjà évaluée. Deux
mécanismes distincts, deux ensembles de règles à connaître.
Destructuring après dynamic import
// Import dynamique → la valeur peut être destructurée comme un objet normal
const { default: HeavyComponent, helper } = await import('./HeavyComponent');
// default = export default, helper = export named helper
Imports CommonJS (Node legacy)
// require() retourne un objet — destructuring classique applicable
const { readFile, writeFile } = require('node:fs/promises');
const { default: chalk } = require('chalk'); // pour les libs ES modules
Mini-projet appliqué — destructuring dans un client API
Pour ancrer tous les patterns vus dans un cas concret, voici un client HTTP complet qui exploite le destructuring à chaque niveau : signature de fonction, traitement de la réponse, gestion d'erreur, parsing des headers. C'est exactement le squelette qu'on retrouve dans Axios, ky, ofetch, et les wrappers fetch internes des SaaS modernes.
1. Signature de la fonction — destructuring des options avec valeurs par défaut
// Options de requête avec valeurs par défaut + rest pour les headers custom
async function apiClient(
url,
{
method = 'GET',
body = null,
timeout = 5000,
retry = 0,
headers: customHeaders = {},
...rest // params, cache, credentials...
} = {}
) {
const headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
...customHeaders, // les headers custom écrasent les defaults
};
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, {
method,
headers,
body: body !== null ? JSON.stringify(body) : undefined,
signal: controller.signal,
...rest,
});
return await handleResponse(res);
} finally {
clearTimeout(timeoutId);
}
}
2. Traitement de la réponse — destructuring des champs essentiels
async function handleResponse(res) {
// Destructuring sur Response
const { ok, status, statusText, headers } = res;
// Destructuring d'un Map-like (Headers) via .get()
const contentType = headers.get('content-type') ?? '';
if (!ok) {
// Erreur : tenter de lire le body comme JSON, fallback texte
const errorBody = contentType.includes('json')
? await res.json()
: await res.text();
// Destructuring avec valeurs par défaut sur le body d'erreur
const { message = statusText, code = 'UNKNOWN', details = null } = errorBody ?? {};
throw new ApiError({ status, code, message, details });
}
if (status === 204) return null; // No Content
return contentType.includes('json') ? res.json() : res.text();
}
3. Classe d'erreur — destructuring dans le constructeur
Pour rappel, les classes JS combinent bien avec les closures vues précédemment — voir le guide des closures et scope pour le détail.
class ApiError extends Error {
// Destructuring direct des paramètres du constructeur
constructor({ status, code, message, details = null }) {
super(message);
this.name = 'ApiError';
this.status = status;
this.code = code;
this.details = details;
}
// Méthode utilitaire avec destructuring sur l'instance
toLogEntry() {
const { status, code, message, details } = this;
return { type: 'api_error', status, code, message, details, ts: Date.now() };
}
}
4. Utilisation côté consommateur — destructuring de la réponse paginée
// Endpoint paginé classique : { data, meta: { page, total, hasMore } }
const {
data: users = [], // valeur par défaut si pas de data
meta: {
page = 1,
total = 0,
hasMore = false,
} = {}, // protection contre meta undefined
} = await apiClient('/api/users?page=1', { timeout: 3000 });
console.log(`Page ${page}/${Math.ceil(total / 20)} — ${users.length} users`);
if (hasMore) console.log('Plus de pages disponibles');
5. Hook React custom — destructuring de l'état + actions
Pour la mécanique des hooks et closures associées, voir le guide des closures.
function useUsers({ pageSize = 20, autoLoad = true } = {}) {
const [state, setState] = useState({
users: [],
loading: false,
error: null,
page: 1,
});
const fetchPage = useCallback(async (page = 1) => {
setState(prev => ({ ...prev, loading: true, error: null }));
try {
const { data: users, meta: { total } } = await apiClient(
`/api/users?page=${page}&size=${pageSize}`
);
setState({ users, total, loading: false, error: null, page });
} catch (e) {
// Destructuring du catch error (TypeScript : e: unknown)
const { message = 'Unknown error', code = 'ERR' } = e instanceof ApiError ? e : { message: String(e) };
setState(prev => ({ ...prev, loading: false, error: { message, code } }));
}
}, [pageSize]);
useEffect(() => { if (autoLoad) fetchPage(1); }, [autoLoad, fetchPage]);
return { ...state, fetchPage };
}
// Consommation — destructuring du retour du hook
function UsersTable() {
const { users, loading, error, page, fetchPage } = useUsers({ pageSize: 50 });
// ...
}
6. Server-side avec Node.js — destructuring des middleware
Pour les patterns de typage des handlers Express + TypeScript, voir le guide JWT Node.js + Angular.
// Express middleware avec destructuring des params, query, body
app.get('/api/users/:userId/orders', async (req, res) => {
const {
params: { userId },
query: { status = 'all', limit = '10', cursor = null },
headers: { authorization = '' },
} = req;
// Validation rapide
if (!authorization.startsWith('Bearer ')) {
return res.status(401).json({ code: 'AUTH_MISSING', message: 'Token requis' });
}
const { id: tokenUserId, role } = verifyToken(authorization.slice(7));
if (tokenUserId !== userId && role !== 'admin') {
return res.status(403).json({ code: 'FORBIDDEN' });
}
const orders = await db.orders.findByUser(userId, { status, limit: Number(limit), cursor });
res.json(orders);
});
7. Pattern à éviter — destructuring qui masque les bugs
// ❌ MAUVAIS : valeur par défaut qui masque une erreur d'API
const { data: users = [] } = await fetchUsers();
// Si l'API renvoie { error: '...' } sans data, users devient [] silencieusement
// → l'utilisateur voit "Aucun utilisateur" au lieu d'un message d'erreur clair
// ✅ BON : destructurer puis VÉRIFIER explicitement
const response = await fetchUsers();
if (response.error) {
throw new ApiError(response.error);
}
const { data: users } = response;
// → erreur propagée correctement, users garanti non-null
Pour aller plus loin sur les patterns de gestion d'erreur et de validation runtime, lire également le guide infer + conditional types qui permet de typer le retour de apiClient selon l'URL passée — pattern utilisé par tRPC et les ORM type-safe modernes.
Conclusion
Le destructuring est l'une des features JavaScript les plus rentables introduites par ES2015. Quelques lignes de syntaxe remplacent des dizaines de lignes d'accès propriété par propriété, rendent les signatures de fonction explicites, fonctionnent main dans la main avec TypeScript, et ne coûtent rien en performance. C'est la fondation des codebases JavaScript modernes — React, Vue, Node, Deno — qui en font un usage quotidien.
Le pattern à intérioriser : « si je vais utiliser plus d'une propriété d'un objet,
je destructure ». Cette simple règle transforme votre code en quelques jours
d'usage. Combinée à la valeur par défaut ({ x = 10 }), au renommage
({ x: alias }) et au rest pattern ({ x, ...rest }), vous
couvrez 95 % des besoins quotidiens. Et avec TypeScript pour typer le tout, vous
obtenez un code à la fois concis et type-safe — l'objectif ultime du JavaScript
moderne en 2026.
- Destructurer les paramètres de fonction pour exposer le contrat dans la signature
- Utiliser les valeurs par défaut dans le destructuring (
{ x = 10 }) - Renommer (
{ x: alias }) uniquement quand le nom collisionne - Combiner avec le rest pattern pour transmettre les props restantes (
{ x, ...rest }) - Limiter à 2 niveaux d'imbrication maximum
- Toujours protéger contre
undefined(= {}) sur les params optionnels - Typer avec interface/type plutôt qu'inline pour la réutilisabilité
- Ne pas confondre import nommé (
import { x }) et destructuring d'objet - Utiliser
{ data = null } = response ?? {}pour la safety totale - Apprendre les 10 patterns réels — ils couvrent 95 % des besoins quotidiens