Ce que vous saurez faire à la fin
- Comprendre pourquoi 80% des entreprises tech (Microsoft, Airbnb, Stripe, Wave) imposent TypeScript en 2026 (informations vérifiées en avril 2026, susceptibles d’évoluer) et le valoriser sur votre CV.
- Installer TypeScript dans un projet JavaScript existant sans tout réécrire en 30 minutes maximum.
- Maîtriser les types essentiels (primitifs, objets, unions, génériques, utilitaires) pour 90% des cas d’usage en PME.
- Migrer progressivement un projet legacy de 50 fichiers JS vers TypeScript en gardant la production en marche.
- Configurer VS Code, ESLint et Prettier pour un workflow TypeScript moderne avec autocomplétion intelligente.
Durée : 4h. Pré-requis : JavaScript ES6+ (let, const, fonctions fléchées, async/await), Node.js 20+, npm, VS Code, un projet JS existant pour pratiquer (sinon vous en créerez un). Coût : 0 FCFA.
Étape 1 — Pourquoi TypeScript change tout
JavaScript est dynamiquement typé : une variable peut contenir un nombre puis une chaîne sans erreur. Cette flexibilité devient un cauchemar passé 1000 lignes de code, surtout en équipe. TypeScript ajoute un système de types qui détecte 80% des bugs avant l’exécution, directement dans VS Code.
// JavaScript : pas d'erreur tant que ça ne plante pas
function calculerTVA(prix, taux) {
return prix * (1 + taux);
}
calculerTVA("5000", "0.18"); // résultat : "50000.18" (concaténation !)
calculerTVA(); // NaN, pas d'alerte
calculerTVA(5000); // NaN, pas d'alerte
// TypeScript : erreur dès la frappe
function calculerTVA(prix: number, taux: number): number {
return prix * (1 + taux);
}
calculerTVA("5000", "0.18"); // ERREUR : Argument of type 'string' not assignable
calculerTVA(); // ERREUR : Expected 2 arguments, but got 0
calculerTVA(5000, 0.18); // 5900, OK
Étape 2 — Installation dans un projet existant
cd mon-projet-js
npm install -D typescript @types/node tsx
npx tsc --init
# Cela génère tsconfig.json
# Installer les types pour vos dépendances courantes :
npm install -D @types/express @types/lodash
// tsconfig.json (configuration recommandée pour PME)
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"allowJs": true,
"checkJs": false,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Étape 3 — Types primitifs et inférence
// Inférence : TypeScript devine le type
const nom = "Boutique Dakar"; // string
const prix = 4500; // number
const actif = true; // boolean
// Annotation explicite quand nécessaire
let chiffreAffaires: number = 0;
let referenceClient: string;
// Tableaux
const produits: string[] = ["Riz", "Huile", "Tomate"];
const prixFCFA: Array<number> = [450000, 120000, 35000];
// Tuples : longueur fixe avec types par position
const coordonneesDakar: [number, number] = [14.6928, -17.4467];
// any et unknown : à éviter au maximum
let mauvais: any; // désactive TypeScript, perte de garanties
let prudent: unknown; // forcera une vérification avant usage
Étape 4 — Types pour objets et interfaces
// Type alias
type Produit = {
id: number;
nom: string;
prixFCFA: number;
enStock: boolean;
categorie?: string; // optionnel grâce au ?
};
// Interface (équivalent pour objets, extensible)
interface Client {
id: number;
nom: string;
email: string;
telephone: string;
}
interface ClientPremium extends Client {
remise: number;
niveauFidelite: 'bronze' | 'argent' | 'or';
}
const oumar: ClientPremium = {
id: 1,
nom: "Oumar Diop",
email: "oumar@gmail.com",
telephone: "+221 77 123 45 67",
remise: 10,
niveauFidelite: 'or'
};
Étape 5 — Union types et narrowing
// Union : une variable peut être de plusieurs types
type ModePaiement = 'wave' | 'orange-money' | 'free-money' | 'carte' | 'especes';
function fraisPaiement(mode: ModePaiement): number {
switch (mode) {
case 'wave':
case 'orange-money':
case 'free-money':
return 0; // gratuit pour le marchand
case 'carte':
return 200; // frais bancaires
case 'especes':
return 0;
// TypeScript force à couvrir tous les cas
}
}
// Narrowing : TypeScript affine le type au fur et à mesure
function afficherIdentifiant(id: number | string) {
if (typeof id === 'string') {
// Ici, TypeScript sait que id est string
console.log(id.toUpperCase());
} else {
// Ici, id est forcément number
console.log(id.toFixed(2));
}
}
Étape 6 — Génériques pour code réutilisable
// Fonction générique : marche avec n'importe quel type
function premier<T>(liste: T[]): T | undefined {
return liste[0];
}
const premierNom = premier(["Awa", "Moussa"]); // string | undefined
const premierPrix = premier([1500, 3500]); // number | undefined
// Type générique : utilitaire de réponse API
type ReponseAPI<T> = {
ok: boolean;
data?: T;
erreur?: string;
};
async function fetchProduits(): Promise<ReponseAPI<Produit[]>> {
try {
const r = await fetch('/api/produits');
const data: Produit[] = await r.json();
return { ok: true, data };
} catch (e) {
return { ok: false, erreur: (e as Error).message };
}
}
Étape 7 — Types utilitaires intégrés
// Partial : tous les champs deviennent optionnels
function modifierProduit(id: number, modifs: Partial<Produit>) {
// ... mise à jour partielle en base
}
modifierProduit(42, { prixFCFA: 5500 }); // OK même sans nom ni id
// Pick : sélectionner des champs
type ProduitVitrine = Pick<Produit, 'nom' | 'prixFCFA'>;
// Omit : exclure des champs
type ProduitSansId = Omit<Produit, 'id'>;
function creerProduit(p: ProduitSansId): Produit {
return { id: Date.now(), ...p };
}
// Required : tous les champs deviennent obligatoires
type ProduitComplet = Required<Produit>;
// Readonly : empêche la modification
const config: Readonly<{ apiUrl: string }> = {
apiUrl: 'https://api.boutique-dakar.sn'
};
// config.apiUrl = "..."; // ERREUR
// Record : objet avec clés et valeurs typées
const stockParEntrepot: Record<string, number> = {
'Dakar-Centre': 1200,
'Pikine': 850,
'Thies': 450
};
Étape 8 — Migration fichier par fichier
# Stratégie progressive : renommer .js en .ts un par un
mv src/utils/format.js src/utils/format.ts
# Lancer le compilateur en mode watch
npx tsc --noEmit --watch
# Corriger les erreurs UNE PAR UNE
// Avant migration : src/utils/format.js
export function formaterFCFA(montant) {
return montant.toLocaleString('fr-SN') + ' FCFA';
}
// Après migration : src/utils/format.ts
export function formaterFCFA(montant: number): string {
return montant.toLocaleString('fr-SN') + ' FCFA';
}
// Si vous bloquez sur un type complexe : @ts-expect-error TEMPORAIRE
// @ts-expect-error : à typer correctement plus tard (ticket #123)
const result = ancienneAPILegacy.callObscure(input);
Étape 9 — Types pour les API et formulaires
// Définir la forme de données reçues d'une API externe
interface CommandeWave {
id: string;
amount: number;
currency: 'XOF';
status: 'pending' | 'completed' | 'failed';
customer_phone: string;
created_at: string; // ISO 8601
}
async function recupererCommande(id: string): Promise<CommandeWave> {
const r = await fetch(`https://api.wave.com/v1/checkout/sessions/${id}`, {
headers: { Authorization: `Bearer ${process.env.WAVE_KEY}` }
});
if (!r.ok) throw new Error('Wave API error');
return r.json() as Promise<CommandeWave>;
}
// Validation runtime avec Zod (recommandé en production)
import { z } from 'zod';
const ProduitSchema = z.object({
nom: z.string().min(2),
prixFCFA: z.number().int().positive(),
enStock: z.boolean()
});
type ProduitValide = z.infer<typeof ProduitSchema>;
function ajouterProduit(donnees: unknown): ProduitValide {
return ProduitSchema.parse(donnees); // throw si invalide
}
Étape 10 — Types pour React
// Composant fonctionnel typé
type CarteProduitProps = {
produit: Produit;
onAjouter?: (id: number) => void;
};
function CarteProduit({ produit, onAjouter }: CarteProduitProps) {
return (
<article>
<h3>{produit.nom}</h3>
<p>{produit.prixFCFA.toLocaleString()} FCFA</p>
<button onClick={() => onAjouter?.(produit.id)}>
Ajouter au panier
</button>
</article>
);
}
// useState typé
import { useState } from 'react';
const [panier, setPanier] = useState<Produit[]>([]);
const [erreur, setErreur] = useState<string | null>(null);
Étape 11 — Configurer ESLint et Prettier
npm install -D \
eslint @eslint/js typescript-eslint \
prettier eslint-config-prettier
# eslint.config.js
cat > eslint.config.js << 'EOF'
import js from '@eslint/js';
import ts from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default [
js.configs.recommended,
...ts.configs.recommended,
prettier,
{
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
'no-console': ['warn', { allow: ['warn', 'error'] }]
}
}
];
EOF
# Lancer la vérification
npx eslint . --ext .ts,.tsx
Étape 12 — Scripts package.json utiles
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"typecheck": "tsc --noEmit",
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write 'src/**/*.{ts,tsx}'",
"test": "vitest run"
}
}
Le script typecheck est crucial : à chaque commit (via Husky et lint-staged), vous garantissez que le code en production compile sans erreur. Cela bloque 90% des bugs avant qu’ils n’atteignent vos clients.
Étape 13 — Pitfalls et bonnes pratiques
// MAUVAIS : as pour forcer un type sans vérification
const utilisateur = data as User; // dangereux si data invalide
// BON : validation avec type guard
function estUser(x: unknown): x is User {
return typeof x === 'object' && x !== null
&& 'id' in x && 'email' in x;
}
if (estUser(data)) {
// ici TypeScript sait que data est User
}
// MAUVAIS : any partout
function traiter(x: any) { return x.toString(); }
// BON : générique ou unknown
function traiter<T>(x: T): string { return String(x); }
Erreurs fréquentes
- Cannot find module : oubli d’installer @types/xxx pour une dépendance JS sans types intégrés. Solution : npm install -D @types/lodash par exemple.
- Object is possibly undefined : activez noUncheckedIndexedAccess dans tsconfig pour forcer la vérification, et utilisez ?. ou un check explicite.
- Mode strict trop dur d’un coup : activez les options progressivement. Démarrez sans strict, puis strictNullChecks, puis noImplicitAny.
- Mélanger import/require : en TypeScript moderne, utilisez exclusivement import. Le require ne marche que si esModuleInterop est désactivé.
- Builder à la main avec tsc en production : préférez esbuild, swc ou tsx qui sont 10 à 100 fois plus rapides pour les gros projets.
Checklist de validation
- TypeScript installé en dépendance dev avec npm install -D typescript
- tsconfig.json créé avec strict: true
- Au moins 5 fichiers .js renommés en .ts et compilés sans erreur
- Une interface ou type alias défini pour le modèle métier principal
- Un type union utilisé (ex: ModePaiement) à la place d’une chaîne libre
- Une fonction générique écrite et testée
- Validation runtime avec Zod pour au moins un endpoint API
- ESLint configuré avec typescript-eslint et zero erreur
- Script npm run typecheck dans package.json et exécuté avec succès
- VS Code montre l’autocomplétion intelligente sur tous les fichiers .ts
- Documentation interne mise à jour avec les conventions de typage
- Premier déploiement réussi avec build TypeScript en CI/CD
L adoption massive de TypeScript en 2026
TypeScript, projet open-source initie par Microsoft en 2012, est devenu le langage par defaut du developpement JavaScript professionnel. Selon le State of JavaScript 2024, plus de 84 pour cent des developpeurs web l utilisent regulierement, contre 18 pour cent en 2017. Tous les frameworks modernes (Next.js, Vue 3, Svelte, Solid, Nuxt) le supportent nativement et orientent leur documentation autour de lui. Les outils de developpement (VS Code, JetBrains, Cursor) offrent une experience radicalement superieure quand le projet est en TypeScript.
La raison de cette domination tient en une phrase : TypeScript transforme le developpement JavaScript de l ecriture a la conception. Les types declares forcent a reflechir aux structures de donnees avant l implementation, et l IDE devient un partenaire qui detecte les erreurs avant le run-time.
Ce qui change concretement
Autocompletion 10x plus puissante. Avec un type defini sur une variable, l IDE propose immediatement toutes les proprietes et methodes valides. Plus besoin de consulter la doc pour savoir si une chaine accepte trim ou trimStart : l IDE montre. Le gain de vitesse au quotidien est mesurable.
Refactoring fiable. Renommer une propriete d un objet utilise dans 50 fichiers prenait en JS de l incertitude (le find-replace touche du code non concerne). En TS, Rename Symbol de VS Code propage la modification sur toutes les utilisations reelles et ignore le reste. Les grosses bases de code se refactorent sans peur.
Erreurs detectees a la compilation. Passer un null a une fonction qui ne l accepte pas, oublier un parametre obligatoire, utiliser une propriete inexistante : TypeScript le signale avant que le code tourne. Selon une etude Microsoft sur GitHub, environ 15 pour cent des bugs JS sont detectables par les types.
Documentation vivante. Les signatures de fonctions et les types deviennent une documentation toujours a jour, automatiquement verifiee. Un nouveau membre dans l equipe comprend le code plus vite parce que les types racontent l intention.
Comment migrer un projet JavaScript existant
Trois strategies coexistent en 2026.
Strategie 1 — JSDoc + tsc –checkJs. Ajouter des commentaires JSDoc avec annotations de type a vos fichiers JS, et configurer TypeScript pour les analyser via checkJs: true et allowJs: true dans tsconfig.json. C est l adoption progressive sans risque — le code reste du JS executable, mais beneficie des verifications de type. Cible : projets matures qu on ne veut pas reecrire.
Strategie 2 — Migration fichier par fichier. Renommer un fichier .js en .ts, ajouter les types manquants, repeter sur les modules a forte valeur (logique metier critique). Configurer strict: false au depart puis activer progressivement les options strictes (strictNullChecks, noImplicitAny). Cible : projets en developpement actif qui veulent migrer en quelques semaines.
Strategie 3 — Big bang. Reecrire entierement en TS strict. Couteux mais propre. Cible : petits projets ou produits en repensee complete. Sauf cas exceptionnel, la migration progressive est plus realiste.
tsconfig.json — la config qui compte
Le tsconfig.json controle le comportement du compilateur. Quelques options non negociables pour un projet professionnel :
strict: true active tous les checks stricts (strictNullChecks, noImplicitAny, strictFunctionTypes). C est le mode professionnel par defaut.
target: ES2022 ou ES2023 selon les environnements supportes — Node.js 20+ et tous les navigateurs modernes le supportent.
module: NodeNext ou ESNext selon le bundler (Vite, Webpack, esbuild) ou la cible Node.js native.
moduleResolution: bundler ou node16 selon le contexte. Bundler est le choix moderne pour Vite, Next.js, esbuild.
esModuleInterop: true et skipLibCheck: true evitent une serie de problemes courants avec les modules tiers.
noUncheckedIndexedAccess: true est un parametre defensif qui force a verifier qu un acces array[i] peut etre undefined — evite une famille classique de bugs en production.
Les patterns idiomatiques a maitriser
Apres les types primitifs (string, number, boolean) et les types objet basiques, six patterns reviennent dans tout projet TS serieux.
Unions discriminees pour modeliser les etats : type State = {status: 'loading'} | {status: 'success', data: T} | {status: 'error', message: string}. Plus expressif et plus sur que des champs optionnels.
Generics pour les fonctions reutilisables : function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>. Maitriser les generics est ce qui differencie un dev TS junior d un senior.
Type guards pour les verifications runtime : function isString(x: unknown): x is string. Permet au compilateur de comprendre les narrowings.
Utility types du standard : Partial, Required, Readonly, Pick, Omit, Record, Exclude. Memoriser leur usage evite de reinventer la roue.
Inferer les types depuis les valeurs avec typeof, as const, et satisfies. Le pattern const config = {...} as const satisfies Config est devenu standard pour la config typee.
Bibliotheques de validation runtime : Zod (dominant en 2026), Valibot (plus leger), Effect Schema (ecosysteme complet). Permettent de transformer un schema runtime en type TypeScript automatiquement — fondamental pour la securite des API et des formulaires.
FAQ
TypeScript est-il plus lent que JavaScript ?
Non en runtime (les types sont effaces a la compilation). Oui en build (la compilation TS prend du temps). Sur les gros projets, le compilateur peut prendre 30-120 secondes. Les outils comme esbuild ou SWC remplacent tsc pour le transpiling (plus rapide) tout en gardant tsc pour la verification de types (en arriere-plan).
Faut-il TS pour un petit projet personnel ?
Pour un script de moins de 100 lignes, JS suffit. Au-dela, TypeScript paie son ticket d entree en moins de quelques heures par l autocompletion et la securite. Pour un projet en equipe ou un produit destine a evoluer, TS est obligatoire.
Quelle difference entre TypeScript et Flow ?
Flow (Meta) a perdu la guerre des types JS en 2020-2022. TypeScript a gagne par l ecosysteme, les outils, et la documentation. Flow est encore utilise dans certains projets historiques (Meta, mais en migration interne vers TS).
TS et Deno / Bun ?
Deno et Bun executent TypeScript nativement sans build step. C est un argument fort pour ces runtimes alternatifs. En 2026, Node.js 22+ supporte aussi TS sans compilation (avec –experimental-strip-types).
References
- TypeScript Handbook officiel — typescriptlang.org/docs
- State of JavaScript — stateofjs.com
- Zod — zod.dev
- TypeScript Deep Dive (book) — basarat.gitbook.io/typescript
- TS Config Cheat Sheet — totaltypescript.com/tsconfig-cheat-sheet