Introduction : pourquoi cette question est-elle si fréquente ?
\n\nChaque développeur qui passe de JavaScript à TypeScript, ou qui code simplement dans ce langage, finit tôt ou tard par se heurter à un problème : comment définir correctement une valeur par défaut pour un objet passé en argument à une fonction ? Sur Stack Overflow, cette question cumule des milliers de vues et des dizaines de réponses. Pourquoi ? Parce qu'une tâche en apparence simple — "si l'objet n'est pas passé, utilise un objet vide" — se transforme en TypeScript en un véritable parcours du combattant avec les types, les interfaces et la déstructuration.
\n\nDans cet article, nous allons explorer toutes les méthodes possibles, des plus simples aux plus avancées, avec des exemples de code et des explications claires. Vous apprendrez à configurer des valeurs par défaut pour un objet TypeScript passé en argument et à éviter les erreurs courantes. C'est parti !
\n\nLe problème : qu'est-ce qui cloche avec l'approche classique ?
\n\nImaginons une fonction qui accepte un objet de configuration :
\n\nfunction createUser(config: { name?: string; age?: number }) {\n // ...\n}\n\ncreateUser(); // Erreur ! Un argument est requis\n\n\nEn JavaScript, on pourrait simplement écrire function createUser(config = {}) et le tour est joué. Mais en TypeScript, cette astuce ne fonctionne pas : le compilateur va râler à cause d'une incompatibilité de types. Pourquoi ? Parce que l'objet vide {} ne correspond pas à l'interface { name?: string; age?: number } — il ne contient tout simplement pas ces propriétés.
C'est là que les choses deviennent intéressantes. Passons en revue toutes les solutions.
\n\nMéthode 1 : Déstructuration avec valeurs par défaut
\n\nLa méthode la plus élégante et la plus populaire consiste à utiliser la déstructuration directement dans les paramètres de la fonction. Vous extrayez les propriétés de l'objet et leur assignez immédiatement des valeurs par défaut :
\n\nfunction createUser({ name = "Invité", age = 18 }: { name?: string; age?: number } = {}) {\n console.log(`Utilisateur créé : ${name}, âge : ${age}`);\n}\n\ncreateUser(); // "Utilisateur créé : Invité, âge : 18"\ncreateUser({ name: "Anne" }); // "Utilisateur créé : Anne, âge : 18"\ncreateUser({ age: 25 }); // "Utilisateur créé : Invité, âge : 25"\n\n\nNotez le = {} à la fin. Il s'agit de la valeur par défaut pour l'objet lui-même. Si vous ne passez pas d'argument, TypeScript utilise un objet vide, et la déstructuration applique les valeurs par défaut pour chaque propriété.
Avantages : propre, concis, fonctionne parfaitement.
\nInconvénients : nécessite de spécifier explicitement le type pour chaque propriété.
Méthode 2 : Utilisation d'une interface avec Partial
\n\nSi votre objet est volumineux, décrire tous les types dans les paramètres n'est pas idéal. Sortez l'interface et utilisez Partial<T> :
interface UserConfig {\n name: string;\n age: number;\n email: string;\n isAdmin: boolean;\n}\n\nfunction createUser(config: Partial<UserConfig> = {}) {\n const defaults: UserConfig = {\n name: "Invité",\n age: 18,\n email: "noemail@example.com",\n isAdmin: false\n };\n \n const mergedConfig = { ...defaults, ...config };\n console.log(mergedConfig);\n}\n\ncreateUser({ name: "Jean" });\n// { name: "Jean", age: 18, email: "noemail@example.com", isAdmin: false }\n\n\nIci, Partial<UserConfig> rend toutes les propriétés optionnelles. À l'intérieur de la fonction, nous fusionnons les valeurs par défaut avec celles passées en argument. Cela fonctionne très bien, mais nécessite une fusion manuelle.
Méthode 3 : Approche classique avec vérification
\n\nParfois, vous avez besoin de plus de contrôle. Par exemple, si vous souhaitez définir une valeur par défaut uniquement pour undefined, mais pas pour null. Utilisons le bon vieux if :
function createUser(config?: UserConfig) {\n const name = config?.name ?? "Invité";\n const age = config?.age ?? 18;\n const email = config?.email ?? "noemail@example.com";\n \n console.log({ name, age, email });\n}\n\ncreateUser();