TypeScript : projets Node.js et web
Les types disparaissent à l’exécution
Section intitulée « Les types disparaissent à l’exécution »TypeScript vérifie le code avant exécution. Toute donnée qui vient de l’extérieur (corps de requête, JSON d’une API, variables d’environnement, base de données, localStorage) doit être validée à l’exécution : un type seul n’apporte aucune garantie.
const client = (await reponse.json()) as Client; // aucune vérification : peut mentirValidation avec Zod
Section intitulée « Validation avec Zod »npm install zodimport { z } from 'zod';
const SchemaClient = z.object({ id: z.number().int().positive(), nom: z.string().trim().min(1).max(100), email: z.string().email().optional(),});
type Client = z.infer<typeof SchemaClient>; // le type est DÉRIVÉ du schéma
const resultat = SchemaClient.safeParse(JSON.parse(texte));if (!resultat.success) { console.error(resultat.error.issues);} else { const client: Client = resultat.data; // type vérifié à l'exécution}z.infer produit le type à partir du schéma : une seule source de vérité pour le type et la validation. Voir Express : API JSON.
API Express typée
Section intitulée « API Express typée »npm install expressnpm install -D @types/express tsximport express, { type NextFunction, type Request, type Response,} from 'express';import { z } from 'zod';
const app = express();app.use(express.json({ limit: '100kb' }));
const SchemaCreation = z.object({ nom: z.string().min(1), email: z.string().email(),});type Creation = z.infer<typeof SchemaCreation>;
app.post('/api/clients', (req: Request, res: Response) => { const resultat = SchemaCreation.safeParse(req.body); if (!resultat.success) { res .status(400) .json({ erreur: 'données invalides', details: resultat.error.issues }); return; } const donnees: Creation = resultat.data; res.status(201).json({ id: 1, ...donnees });});
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => { console.error(err); res.status(500).json({ erreur: 'erreur interne' });});
app.listen(3000, '127.0.0.1');Voir Express : installation et Express : sécurité et production.
Variables d’environnement typées
Section intitulée « Variables d’environnement typées »import { z } from 'zod';
const SchemaEnv = z.object({ NODE_ENV: z .enum(['development', 'production', 'test']) .default('development'), PORT: z.coerce.number().int().min(1).max(65535).default(3000), DATABASE_URL: z.string().url(),});
export const env = SchemaEnv.parse(process.env); // échoue au démarrage si une variable est invalideValider l’environnement au démarrage : une configuration incorrecte fait échouer l’application immédiatement, avec un message clair. env.PORT est alors un number garanti.
Erreurs typées
Section intitulée « Erreurs typées »class ErreurHttp extends Error { constructor( public readonly statut: number, message: string, ) { super(message); this.name = 'ErreurHttp'; }}
try { throw new ErreurHttp(404, 'client introuvable');} catch (erreur: unknown) { if (erreur instanceof ErreurHttp) console.error(erreur.statut, erreur.message); else throw erreur;}En TypeScript, la variable d’un catch est de type unknown (avec strict) : l’affiner avant usage. Le mot-clé public dans les paramètres du constructeur est une syntaxe qui génère du code (voir l’avertissement sur l’exécution directe dans Installation).
Résultat plutôt qu’exception
Section intitulée « Résultat plutôt qu’exception »type Resultat<T, E = Error> = { ok: true; valeur: T } | { ok: false; erreur: E };
function diviser(a: number, b: number): Resultat<number, string> { return b === 0 ? { ok: false, erreur: 'division par zéro' } : { ok: true, valeur: a / b };}
const r = diviser(1, 0);if (r.ok) console.log(r.valeur);else console.error(r.erreur);L’union discriminée oblige l’appelant à traiter l’échec (Types de base).
React avec TypeScript
Section intitulée « React avec TypeScript »import { useState, type ReactNode } from 'react';
interface Props { titre: string; enfants?: ReactNode; onValider: (valeur: string) => void;}
export function Formulaire({ titre, enfants, onValider }: Props) { const [valeur, setValeur] = useState('');
return ( <form onSubmit={(e) => { e.preventDefault(); onValider(valeur); }} > <h2>{titre}</h2> <input value={valeur} onChange={(e) => setValeur(e.target.value)} /> {enfants} </form> );}useState<string | null>(null) précise l’état s’il ne peut pas être déduit. Voir React : composants et JSX.
Les fichiers .astro et .ts sont typés ; vérifier avec :
npx astro check---interface Props { titre: string; tags?: string[];}const { titre, tags = [] } = Astro.props;---Voir Astro : composants et layouts.
Next.js génère les types des routes et des pages ; les paramètres de route sont des promesses dans les versions récentes :
type Props = { params: Promise<{ slug: string }> };
export default async function Page({ params }: Props) { const { slug } = await params; return <h1>{slug}</h1>;}Voir Next.js : routage avec l’App Router.
Construire et vérifier
Section intitulée « Construire et vérifier »| Étape | Commande |
|---|---|
| Vérification des types | tsc --noEmit (ou astro check, next build) |
| Analyse du code | eslint . avec typescript-eslint (ESLint : TypeScript) |
| Formatage | prettier --check . |
| Tests | vitest run (Vitest : installation) |
| Construction | tsc, ou l’outil du framework |
Dans l’intégration continue, séparer types, analyse, tests : un échec indique immédiatement la cause (voir Husky : intégration continue et équipes).
Bibliothèques courantes
Section intitulée « Bibliothèques courantes »| Besoin | Bibliothèque |
|---|---|
| Validation de données | Zod, Valibot |
| Exécuter du TypeScript | tsx, Node.js (types effacés) |
| Tests | Vitest |
| Requêtes SQL typées | Kysely, Drizzle |
| Client d’API typé | Génération à partir d’une spécification OpenAPI |
| Dates | date-fns, Temporal (selon la version du moteur) |
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
Property 'body' does not exist ou types d’Express manquants |
npm install -D @types/express |
req.body de type any |
Valider avec un schéma Zod (le type de any n’est pas une garantie) |
| Types qui divergent des données réelles | Type écrit à la main et non validé : dériver du schéma |
process.env.X est string | undefined |
Valider l’environnement au démarrage |
Les tests passent, tsc échoue |
Vitest ne vérifie pas les types : tsc --noEmit en CI |
Cannot find module après le build |
Extensions dans les imports (.js avec NodeNext) |
| Build lent | incremental: true, skipLibCheck, limiter include |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune propre à Ubuntu. Pour déployer, compiler (tsc) puis exécuter node dist/index.js sous systemd (voir Express : sécurité et production) ; le code compilé ne dépend plus de TypeScript.