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
.tspour les exports et imports de types explicites ; réservez les fichiers.d.tsstrictement aux déclarations ambiantes comme les variables d’environnement ou les augmentations globales. - Adoptez une convention de nommage cohérente telle que
*.types.tspour les types spécifiques à un module et des fichiers barrelindex.tspour des chemins d’import propres et stables. - Dans les monorepos ou les packages partagés, exposez les types via la condition
typesdans les exports dupackage.jsonet 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.tspartagé à 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.
Discover how at OpenReplay.com.
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 variablesimport.meta.envpour Vite ou des bundlers similairesglobal.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 :
| Pattern | Exemple | Cas d’usage |
|---|---|---|
*.types.ts | user.types.ts | Types spécifiques à un module |
index.ts | types/index.ts | Ré-export barrel |
env.d.ts | env.d.ts | Déclarations de variables d’environnement |
global.d.ts | global.d.ts | Augmentation 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.