Introduction : pourquoi la question des @types/* tourmente même les développeurs expérimentés ?
Avez-vous déjà passé une heure à déboguer une erreur qui, en fin de compte, se résumait à un mauvais placement du package @types/* dans package.json ? Si oui, vous n'êtes pas seul. La question « How do I decide whether @types/* goes into dependencies or devDependencies? » a rassemblé des milliers de vues sur Stack Overflow et est devenue un véritable casse-tête pour la communauté TypeScript.
À première vue, quoi de plus simple ? On met les types dans devDependencies et le tour est joué. Mais ce n'est pas si simple. Lorsque votre projet commence à grandir, que des bibliothèques tierces, des monorepos et des pipelines CI/CD s'ajoutent, un mauvais placement des types peut entraîner des erreurs de build fatales, des plantages inattendus en production ou, pire encore, une perte silencieuse du typage.
Dans cet article, nous allons décortiquer les 5 erreurs les plus fréquentes lors du travail avec @types/*, apprendre à prendre la bonne décision en fonction de scénarios réels et établir une liste de contrôle qui vous protégera des problèmes. Prêt ? C'est parti !
Erreur n°1 : « Tous les @types/* sont toujours dans devDependencies » — un mythe qui casse les builds
Le conseil le plus populaire sur Internet est : « Les types ne sont pas du code runtime, ils doivent donc toujours être dans devDependencies ». Et c'est vrai dans 90% des cas. Mais il y a une exception importante que l'on oublie souvent.
Quand devDependencies est le bon choix
Si vous écrivez une application (serveur web avec Express, frontend React, utilitaire en ligne de commande), alors tous les @types/* peuvent aller sans crainte dans devDependencies. Votre utilisateur final ne compile pas TypeScript — il exécute du JavaScript déjà compilé. Les types ne sont nécessaires que pendant le développement.
// Exemple pour une application avec Express{ "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.11.0", "typescript": "^5.3.3" }}Quand devDependencies est une erreur fatale
Imaginez maintenant : vous développez une bibliothèque qui exporte des types TypeScript pour des consommateurs externes. Si les types de votre bibliothèque dépendent de @types/express et que vous l'avez mis dans devDependencies, alors l'utilisateur qui installe votre bibliothèque ne recevra pas ces types automatiquement. Et son IDE commencera à afficher des soulignements rouges.
// ❌ Incorrect pour une bibliothèque qui exporte des types{ "devDependencies": { "@types/express": "^4.17.21" // L'utilisateur ne verra pas ces types ! }}
// ✅ Correct{ "dependencies": { "@types/express": "^4.17.21" // Les types seront disponibles pour les consommateurs }}Règle d'or : Si votre bibliothèque réexporte des types d'un package @types/* ou les utilise dans son API publique, alors ce package doit être dans dependencies. Si les types sont utilisés uniquement en interne — devDependencies.
Erreur n°2 : Ignorer la différence entre « j'utilise dans le code » et « j'utilise dans les types »
De nombreux développeurs confondent : « J'importe Request depuis express, donc express doit être dans dependencies ». C'est vrai pour le package express lui-même, mais pas pour ses types.
Prenons un exemple concret. Il y a trois scénarios d'utilisation du package @types/lodash :
- Scénario A : Vous écrivez
import _ from 'lodash'et utilisez_.chunk(). Les types ne sont nécessaires que pour la compilation —devDependencies. - Scénario B : Vous créez une fonction
function mySort(arr: T[]): T[]et utilisez_.orderByà l'intérieur. Les types ne sont pas exposés —devDependencies. - Scénario C : Vous écrivez
export function proc