Mettre en route un projet TypeScript côté frontend demande plus qu’un simple générateur. Le vrai enjeu, c’est d’aligner l’outil de build, le tsconfig.json et la structure de fichiers pour que le code reste lisible, vérifiable et simple à faire évoluer. Je vais aller directement aux choix qui comptent: quel socle prendre, quelles options activer, comment organiser le dossier src et quels pièges éviter dès le départ.
L’essentiel pour partir sur une base TypeScript saine
- Pour une application frontend classique, je pars presque toujours sur Vite avec TypeScript; pour du SSR ou une couche plus intégrée, un framework peut être plus pertinent.
- La combinaison moduleResolution: "bundler", strict: true et noEmit: true couvre l’essentiel des projets orientés navigateur.
- La vérification des types ne doit pas dépendre uniquement du build: je garde
tsc --noEmitou un checker séparé dans les scripts. - Une arborescence simple avec
src, des alias propres et des frontières claires vaut mieux qu’une structure sur-architecturée. - Les erreurs les plus coûteuses viennent souvent du décalage entre ce que l’éditeur accepte, ce que le bundler résout et ce que le navigateur exécute.
Ce qu’il faut décider avant de générer le projet
Avant même de lancer l’outil de démarrage, je décide du cadre technique. Pour un frontend moderne, il faut d’abord savoir si l’on construit une SPA, une interface avec routage avancé, ou une application avec rendu côté serveur. Ce choix change la façon d’initialiser le projet, parce qu’on ne met pas les mêmes attentes sur TypeScript selon que l’on vise un site marketing, un tableau de bord ou une application métier.
En pratique, je distingue trois points de départ. Le plus courant, c’est Vite avec TypeScript, parce que le socle est léger, rapide et facile à adapter. Quand l’application a besoin de SSR, d’API routes ou de conventions plus intégrées, je regarde plutôt un framework comme Next.js ou un équivalent. Et quand le besoin est très minimal, par exemple pour une librairie ou un prototype interne, un projet plus proche de tsc peut suffire, mais ce n’est pas le meilleur choix pour une vraie application frontend.
| Option | Quand je la choisis | Atout principal | Limite |
|---|---|---|---|
| Vite + TypeScript | SPA, dashboard, interface produit | Démarrage rapide, configuration souple, excellent confort de dev | Pas de SSR natif, il faut ajouter le reste selon le besoin |
| Framework intégré | Rendu serveur, SEO plus poussé, routing structuré | Beaucoup de conventions déjà posées | Plus d’opinions, donc plus de contraintes si l’équipe veut tout personnaliser |
tsc seul |
Librairie, package interne, cas très simple | Minimalisme total | Pas suffisant pour une app navigateur sans autre outillage |
Pour un frontend standard, je préfère nettement Vite avec un template TypeScript, parce que je peux partir vite sans sacrifier la clarté. Une fois ce socle posé, le vrai travail commence dans le fichier de configuration.
Le fichier tsconfig.json qui évite les mauvaises surprises
La configuration TypeScript n’est pas un détail administratif. C’est elle qui décide si le compilateur comprend vraiment la logique du navigateur, si les erreurs remontent assez tôt et si l’équipe peut évoluer sans accumuler des exceptions. La documentation TypeScript insiste d’ailleurs sur un point simple: le tsconfig.json définit à la fois la racine du projet et les options de compilation. C’est exactement pour cela qu’il faut le traiter comme une base de produit, pas comme un fichier généré puis oublié.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"isolatedModules": true,
"sourceMap": true,
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"]
}Les options qui comptent vraiment sont assez faciles à résumer. strict ferme la porte à beaucoup d’erreurs silencieuses. moduleResolution: "bundler" aligne la résolution des imports sur ce que fait un bundler moderne, ce qui évite les faux positifs inutiles en frontend. noEmit est utile quand le build final est confié à Vite ou à un autre outil, parce que TypeScript reste alors concentré sur le typage. Et isolatedModules aide à repérer les patterns qui passent mal dans des chaînes de transpilation réelles.
Je garde jsx: "react-jsx" si le projet est en React; dans d’autres écosystèmes, cette option peut disparaître ou être gérée autrement. J’ajoute parfois noUncheckedIndexedAccess ou exactOptionalPropertyTypes sur des bases déjà mûres, mais pas le premier jour: je préfère d’abord une config qui protège sans enfermer l’équipe dans une rigueur excessive. Une fois ces règles en place, il faut encore organiser les fichiers pour que l’architecture ne se transforme pas en labyrinthe.

Une structure de dossier simple à faire évoluer
Je vois souvent des projets qui se compliquent trop tôt. Beaucoup d’équipes créent dix dossiers avant d’avoir écrit une vraie fonctionnalité, puis passent leur temps à débattre de l’endroit où ranger un hook, un type ou un utilitaire. Je préfère l’approche inverse: une structure claire, peu profonde, et des frontières qui deviennent plus strictes seulement quand le projet grossit.
src/
components/
features/
hooks/
lib/
styles/
types/
main.tsx
App.tsxCette base fonctionne bien pour un produit frontend classique. components regroupe les briques réutilisables, features contient ce qui est lié à un domaine métier précis, lib accueille les helpers stables et types évite de disperser les contrats de données. Si le projet est encore petit, je n’ouvre pas tous ces dossiers d’un coup: je garde seulement ce qui sert immédiatement, puis j’élargis au bon moment.
L’alias @/ change aussi beaucoup de choses au quotidien. Il évite les imports du type ../../../, qui deviennent vite illisibles et fragiles. Le point important, c’est que l’alias ne doit pas exister uniquement dans tsconfig.json; il faut aussi le synchroniser avec le bundler. Sinon, l’éditeur comprendra le chemin, mais le build cassera. C’est un détail, mais c’est souvent le détail qui ralentit le plus les équipes.
Quand la structure est lisible et que les imports restent courts, les scripts de travail deviennent beaucoup plus simples à exploiter.
Des scripts de travail qui gardent le rythme
Un projet TypeScript bien initialisé n’a pas seulement besoin de compiler. Il doit aussi permettre de développer vite, de vérifier les types sans ambiguïté et de préparer le build sans surprise. La doc Vite rappelle d’ailleurs un point utile: Vite transpile TypeScript très rapidement, mais la vérification de types doit rester un processus à part si l’on veut autre chose que de simples retours de l’éditeur. C’est une séparation saine, et je la garde presque toujours.
{
"scripts": {
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit",
"preview": "vite preview",
"lint": "eslint ."
}
}Ce découpage me donne un rythme de travail propre. dev lance l’environnement local. build produit l’artefact final. typecheck bloque les erreurs de typage sans générer de fichiers inutiles. lint garde une ligne de style cohérente. Si je veux voir les erreurs de types plus tôt, j’ajoute soit un processus tsc --noEmit --watch, soit un checker intégré au navigateur. Dans un bon setup, le hot module replacement peut même descendre sous les 50 ms, ce qui change vraiment la sensation de travail sur un frontend.
Je garde aussi un œil sur les variables d’environnement. Avec Vite, je m’en tiens à la convention VITE_ pour exposer ce qui doit l’être, et je déclare clairement les types si l’application dépend de plusieurs environnements. C’est le genre de discipline qui évite les bricolages au moment des déploiements.
Une base de scripts propre réduit déjà beaucoup de friction, mais les vraies pertes de temps viennent souvent d’erreurs plus discrètes, donc plus coûteuses.
Les erreurs qui coûtent le plus cher quand on démarre
Les problèmes les plus pénibles ne sont pas toujours les plus visibles. Sur les projets frontend TypeScript, je retrouve souvent les mêmes mauvaises habitudes: on confond transpilation et vérification des types, on crée des alias sans les répercuter dans le bundler, ou on baisse la barre de rigueur pour aller plus vite. Le résultat est classique: tout semble marcher jusqu’au premier refactor un peu sérieux.
| Erreur fréquente | Pourquoi ça pose problème | Ce que je fais à la place |
|---|---|---|
Laisser strict désactivé |
Les erreurs restent cachées trop longtemps | Je pars directement avec une base stricte, puis j’assouplis seulement si le besoin est réel |
| Penser que le build vérifie les types | Le code peut compiler alors que le typage est cassé | Je garde tsc --noEmit dans les scripts ou dans la CI |
| Déclarer un alias seulement dans TypeScript | L’éditeur comprend les imports, mais pas le bundler | Je synchronise l’alias dans tsconfig.json et dans la config du bundler |
| Utiliser des casts trop tôt | Les as masquent les vrais problèmes de données |
Je préfère d’abord typer correctement, puis valider les entrées au besoin |
| Oublier la validation runtime | TypeScript ne protège pas contre une API qui renvoie des données invalides | Je valide les payloads aux frontières, surtout quand le frontend dépend d’un backend externe |
Le dernier point mérite d’être dit clairement: les types ne sécurisent pas les données à l’exécution. Si le frontend consomme une API, je considère la validation runtime comme une barrière normale, pas comme un luxe. C’est souvent là que la différence se fait entre un projet qui “compile” et un projet qui tient réellement en production.
Ce que je garderais pour un frontend TypeScript en 2026
En 2026, je partirais encore sur un socle simple et robuste: Vite pour la majorité des interfaces frontend, un tsconfig.json strict et aligné sur le bundler, des alias courts, puis des scripts séparés pour le build et la vérification de types. Ce n’est pas la configuration la plus spectaculaire, mais c’est celle qui permet d’avancer sans passer son temps à réparer la base.
- Choisir d’abord le bon cadre, sans mélanger SPA, SSR et librairie dans la même décision.
- Configurer TypeScript pour le navigateur, pas pour un monde théorique.
- Garder la structure de dossiers lisible tant que le volume ne justifie pas plus de complexité.
- Mettre la vérification de types à part du build, puis l’automatiser dans la routine de dev ou la CI.
Si je devais résumer la méthode en une phrase, je dirais ceci: la meilleure initialisation d’un projet TypeScript frontend n’est pas celle qui empile le plus d’outils, mais celle qui supprime les ambiguïtés dès le départ. C’est ce qui donne un code plus stable, des refactors plus rapides et, au final, une équipe qui travaille avec moins de bruit et plus de confiance.