Back

Comment organiser les définitions de types dans un projet TypeScript

Comment organiser les définitions de types dans un projet TypeScript

La plupart des projets TypeScript démarrent de la même manière : un unique fichier types.ts qui gonfle progressivement jusqu’à atteindre des centaines de lignes. Trouver quoi que ce soit devient une corvée. Réutiliser des types entre fichiers semble risqué. Les nouveaux membres de l’équipe ne savent pas où chercher.

La bonne nouvelle, c’est qu’organiser les types TypeScript ne nécessite pas un système complexe. Cela nécessite une règle claire : placer les types aussi près que possible de l’endroit où ils sont utilisés, et ne les déplacer que lorsqu’ils sont nécessaires ailleurs.

Voici comment appliquer cette règle à chaque étape d’un projet.

Points clés à retenir

  • Placez les types aussi près que possible de leur utilisation et ne les promouvez vers des emplacements partagés que lorsque plusieurs fichiers en ont besoin.
  • Utilisez des fichiers .ts pour les exports et imports de types explicites ; réservez les fichiers .d.ts strictement aux déclarations ambiantes comme les variables d’environnement ou les augmentations globales.
  • Adoptez une convention de nommage cohérente telle que *.types.ts pour les types spécifiques à un module et des fichiers barrel index.ts pour des chemins d’import propres et stables.
  • Dans les monorepos ou les packages partagés, exposez les types via la condition types dans les exports du package.json et gardez les types internes privés.

La décision fondamentale : où un type doit-il résider ?

Posez-vous une question : combien de fichiers ont besoin de ce type ?

  • Un seul fichier → définissez-le en ligne dans ce fichier
  • Quelques fichiers dans le même module → colocalisez-le dans un fichier .types.ts partagé à proximité
  • Dans tout le projet → déplacez-le vers un répertoire partagé src/types/
  • Entre plusieurs packages → publiez-le depuis un package de types dédié

Cette progression maintient les définitions de types de votre projet organisées sans sur-ingénierie dès le départ.

Pattern 1 : types en ligne pour un usage local

Si un type n’est utilisé que dans un seul fichier, définissez-le à cet endroit. Pas besoin de fichier séparé.

// UserCard.tsx
interface UserCardProps {
  name: string
  avatarUrl: string
  role: "admin" | "viewer"
}

export function UserCard({ name, avatarUrl, role }: UserCardProps) {
  // ...
}

C’est le choix par défaut approprié. Une abstraction prématurée crée de l’indirection sans bénéfice.

Pattern 2 : fichiers de types colocalisés pour les types partagés d’un module

Lorsque deux composants ou plus dans le même dossier partagent des types, extrayez-les dans un fichier .types.ts colocalisé.

src/components/user/
├── UserCard.tsx
├── UserList.tsx
└── user.types.ts       ← types partagés pour ce module

Cela maintient les types liés proches du code qui les utilise sans polluer un fichier global.

Pattern 3 : un répertoire types/ partagé pour les définitions transversales

Lorsque des types sont utilisés dans plusieurs modules non liés, un répertoire central src/types/ a du sens. Utilisez un fichier barrel index.ts pour garder les imports propres et stables.

src/types/
├── index.ts            ← ré-exporte tout
├── api.types.ts
├── user.types.ts
└── product.types.ts
// src/types/index.ts
export type { ApiResponse, PaginatedResult } from "./api.types"
export type { User, UserRole } from "./user.types"

Les consommateurs importent depuis un seul chemin : import type { User } from "@/types". Si vous réorganisez ultérieurement les éléments internes, les chemins d’import ne changent pas.

Faut-il utiliser .ts ou .d.ts pour les définitions de types ?

C’est l’un des points de confusion les plus courants dans l’organisation des types TypeScript.

Utilisez des fichiers .ts pour les types que vous exportez et importez explicitement. C’est le bon choix pour presque tout dans une application.

Utilisez des fichiers .d.ts principalement pour les déclarations ambiantes — situations où vous devez indiquer à TypeScript quelque chose qui existe à l’exécution mais n’a pas de source TypeScript. Exemples courants :

  • env.d.ts — déclaration des variables import.meta.env pour Vite ou des bundlers similaires
  • global.d.ts — augmentation des types de modules tiers ou déclaration de variables globales
// env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
  readonly VITE_API_URL: string
}

Certains projets modernes configurent compilerOptions.types explicitement pour contrôler quels types ambiants sont chargés, tandis que d’autres s’appuient sur la découverte automatique de @types. Si vous utilisez une chaîne d’outils basée sur un bundler comme Vite ou Next.js, suivez ses conventions recommandées pour ces fichiers plutôt que de créer des déclarations globales autonomes.

Conventions de nommage qui passent à l’échelle

Un nommage cohérent réduit la charge cognitive au sein d’une équipe :

PatternExempleCas d’usage
*.types.tsuser.types.tsTypes spécifiques à un module
index.tstypes/index.tsRé-export barrel
env.d.tsenv.d.tsDéclarations de variables d’environnement
global.d.tsglobal.d.tsAugmentation de types tiers

Évitez les préfixes IUser et les suffixes UserType. Des noms simples comme User et ApiResponse sont plus propres et s’alignent avec les conventions TypeScript modernes.

Organiser les types TypeScript dans un package partagé

Si vous construisez une bibliothèque ou travaillez dans un monorepo, exposez les types via les exports du package.json en utilisant la condition types plutôt que de vous appuyer sur les anciens patterns typesVersions.

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  }
}

Gardez les types internes privés — ne les exportez pas depuis le point d’entrée du package. Seule la surface d’API publique doit être visible pour les consommateurs.

Conclusion

Commencez avec des types en ligne. Extrayez vers un fichier colocalisé lorsqu’un deuxième fichier en a besoin. Promouvez vers src/types/ lorsqu’ils sont vraiment transversaux. Utilisez des fichiers .d.ts uniquement pour les déclarations ambiantes, pas comme emplacement par défaut pour toutes les définitions de types.

L’objectif de l’organisation des types TypeScript n’est pas une structure de dossiers parfaite — c’est de permettre au prochain développeur (ou à votre futur vous) de trouver et réutiliser des types sans friction.

FAQ

Non. Un fichier unique fonctionne au début mais devient difficile à naviguer à mesure qu'un projet grandit. Au lieu de cela, gardez les types proches de l'endroit où ils sont utilisés. Définissez-les en ligne pour un usage dans un seul fichier, extrayez-les vers un fichier .types.ts colocalisé lorsqu'ils sont partagés au sein d'un module, et ne les promouvez vers un répertoire de types central que lorsqu'ils sont nécessaires dans des parties non liées du code.

Utilisez des fichiers .ts pour les types que vous exportez et importez explicitement dans votre code applicatif. Utilisez des fichiers .d.ts uniquement pour les déclarations ambiantes, comme la description de variables d'environnement ou l'augmentation de types de modules tiers. Placer des types applicatifs réguliers dans des fichiers .d.ts peut créer de la confusion car ces déclarations sont disponibles globalement sans import explicite.

Une convention largement adoptée consiste à utiliser le pattern nom-du-module.types.ts pour les types spécifiques à un module et un fichier barrel index.ts pour les ré-exports dans un répertoire de types partagé. Évitez la notation hongroise comme IUser ou les suffixes redondants comme UserType. Des noms simples et descriptifs tels que User et ApiResponse sont préférés dans les projets TypeScript modernes.

Utilisez la condition types à l'intérieur du champ exports de votre package.json pour pointer vers votre fichier de déclaration compilé. Cette approche est plus explicite et fiable que l'ancien pattern typesVersions. N'exportez que les types qui font partie de votre API publique et gardez les types internes privés pour éviter de divulguer des détails d'implémentation aux consommateurs.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay