Contrôler le type des éléments d'un objet indexé TypeScript

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi ce sujet fait-il exploser Stack Overflow ?

\n\n

Si vous avez déjà codé en TypeScript et tenté de créer un objet où toutes les valeurs doivent être du même type, vous avez forcément rencontré ce problème. La question "Enforcing the type of the indexed members of a Typescript object" cumule des milliers de vues sur Stack Overflow pour une bonne raison : c'est un classique pour les développeurs venant du JavaScript.

\n\n

Imaginez : vous avez un dictionnaire de cache, une configuration avec des clés dynamiques, ou simplement une map de nombres. Vous voulez que TypeScript vérifie que chaque valeur de l'objet est une chaîne, un nombre ou un type complexe. Mais le compilateur laisse passer des erreurs. Ça vous parle ?

\n\n

Dans cet article, nous allons décortiquer toutes les approches principales pour résoudre ce problème : des simples Index Signatures aux Mapped Types avancés. Vous saurez quand utiliser chaque méthode et quels pièges éviter.

\n\n

Le problème : un objet avec des clés dynamiques

\n\n

Prenons un système de gestion d'utilisateurs. Nous avons un objet où les clés sont les ID des utilisateurs et les valeurs leurs noms :

\n\n
interface UserCache {\n  [userId: string]: string;\n}\n\nconst cache: UserCache = {\n  'user_1': 'Alice',\n  'user_2': 'Bob',\n  // 'user_3': 123; // Erreur ! Doit être une chaîne\n};\n\ncache['user_4'] = 'Charlie'; // OK\ncache['user_5'] = 42; // Erreur ! Le type 'number' n'est pas assignable à 'string'\n
\n\n

Ça semble fonctionner. Mais il y a un détail : Index Signature autorise n'importe quelle clé de type string. Et si on veut limiter les clés autorisées ?

\n\n

Approche 1 : Index Signature — le classique

\n\n

L'Index Signature est la manière la plus simple de typer toutes les valeurs d'un objet. Syntaxe :

\n\n
interface StringMap {\n  [key: string]: string;\n}\n\nconst map: StringMap = {\n  hello: 'world',\n  foo: 'bar',\n};\n
\n\n

Avantages :

\n
    \n
  • Simplicité et lisibilité
  • \n
  • Fonctionne avec un nombre illimité de clés
  • \n
\n\n

Inconvénients :

\n
    \n
  • Impossible de restreindre l'ensemble des clés
  • \n
  • Toutes les valeurs doivent être du même type (ou union)
  • \n
  • Ne gère pas bien les valeurs undefined (prudence requise)
  • \n
\n\n

Erreur fréquente des débutants : essayer d'ajouter une méthode à un tel objet :

\n\n
interface BadMap {\n  [key: string]: string;\n  length: number; // Erreur ! 'number' n'est pas assignable à 'string'\n}\n
\n\n

Cela ne fonctionne pas car length viole le type commun. Pour mélanger les types, utilisez une union ou les Mapped Types.

\n\n

Approche 2 : Record — l'utilitaire intégré

\n\n

TypeScript fournit le type utilitaire Record<K, V> qui fait la même chose, mais avec une syntaxe plus concise :

\n\n
type StringRecord = Record<string, string>;\n\nconst config: StringRecord = {\n  apiUrl: 'https://api.example.com',\n  timeout: '5000', // OK, chaîne\n  // port: 8080, // Erreur !\n};\n\n// On peut restreindre les clés :\ntype AllowedKeys = 'name' | 'email' | 'phone';\ntype UserRecord = Record<AllowedKeys, string>;\n\nconst user: UserRecord = {\n  name: 'Alice',\n  email: 'alice@example.com',\n  phone: '+1234567890',\n  // age: '30', // Erreur ! 'age' n'est pas dans AllowedKeys\n};\n
\n\n

Quand l'utiliser :

\n
    \n
  • Pour créer rapidement un dictionnaire typé
  • \n
  • Quand les clés sont un ensemble spécifique de valeurs (union)
  • \n
  • Pour les configurations avec des champs connus
  • \n
\n\n

Inconvénient : Record ne permet pas d'avoir des types différents pour des clés différentes. Pour cela, utilisez les Mapped Types.

\n\n

Approche 3 : Map — l'alternative ES6

\n\n

Si vous utilisez Map de JavaScript, TypeScript le type parfaitement

Blogs

Book Recommendations