Introduction à TypeScript
TypeScript étend JavaScript en intégrant un système de types statiques. Il offre une détection précoce des erreurs, une assistance améliorée dans les IDE et une maintenabilité accrue pour les projets d'envergure. Un environnement Node.js (version 14 ou supérieure) est requis.
Démarrage rapide
Installer TypeScript
npm install -g typescript # Installation globale du compilateur
tsc --version # Afficher la version
Créer un premier fichier TypeScript
Créez un fichier bonjour.ts :
function afficherMessage(nom: string): void {
console.log(`Salut, ${nom} !`);
}
afficherMessage("Monde");
Compiler et exécuter
tsc bonjour.ts # Génère bonjour.js
node bonjour.js # Affiche "Salut, Monde !"
Fondamentaux de TypeScript
Types de base
let termine: boolean = false;
let quantite: number = 42;
let nom: string = "Alice";
// Tableaux
let chiffres: number[] = [1, 2, 3];
let mots: Array<string> = ["bonjour", "monde"];
// Tuple (longueur et types fixes)
let coordonnees: [string, number] = ["Alice", 30];
// Énumération
enum Couleur { Rouge, Vert, Bleu }
let couleurPreferee: Couleur = Couleur.Vert;
// Types any et unknown
let inconnu: any = 4;
inconnu = "peut-être une chaîne";
// void (fonction sans valeur de retour)
function journal(): void {
console.log("Pas de retour");
}
Interfaces
Définir la structure d'un objet :
interface Utilisateur {
nom: string;
age: number;
email?: string; // Propriété optionnelle
}
function afficherUtilisateur(utilisateur: Utilisateur): void {
console.log(`Nom: ${utilisateur.nom}, Age: ${utilisateur.age}`);
}
const bob: Utilisateur = { nom: "Bob", age: 25 };
afficherUtilisateur(bob);
Classes
Support de la programmation orientée objet :
class Creature {
private nom: string;
constructor(nom: string) {
this.nom = nom;
}
public seDeplacer(distance: number = 0): void {
console.log(`${this.nom} s'est déplacé de ${distance}m.`);
}
}
class Chien extends Creature {
aboyer(): void {
console.log("Ouaf ! Ouaf !");
}
}
const monChien = new Chien("Rex");
monChien.seDeplacer(10);
monChien.aboyer();
Types de fonctions
type Operation = (a: number, b: number) => number;
const addition: Operation = (x, y) => x + y;
console.log(addition(2, 3)); // 5
// Paramètres optionnels et par défaut
function saluer(nom: string, message: string = "Bonjour"): string {
return `${message}, ${nom} !`;
}
console.log(saluer("Alice")); // "Bonjour, Alice !"
Génériques
// Fonction générique
function identifier<T>(valeur: T): T {
return valeur;
}
let resultat1 = identifier<string>("hello");
let resultat2 = identifier(42);
// Interface générique
interface Paire<K, V> {
cle: K;
valeur: V;
}
const paireExemple: Paire<number, string> = { cle: 1, valeur: "Pomme" };
Types union et garde de type
type Identifiant = string | number;
function afficherId(id: Identifiant) {
if (typeof id === "string") {
console.log(`ID chaîne: ${id.toUpperCase()}`);
} else {
console.log(`ID numérique: ${id.toFixed(2)}`);
}
}
afficherId("abc123");
afficherId(3.1415);
Configuration TypeScript (tsconfig.json)
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"strict": true,
"outDir": "./dist",
"esModuleInterop": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
Utilisation avec React
// Props et State typés pour les composants
interface CompteurProps {
valeurInitiale: number;
}
interface CompteurState {
compteur: number;
}
class Compteur extends React.Component<CompteurProps, CompteurState> {
state: CompteurState = {
compteur: this.props.valeurInitiale,
};
incrementer = () => {
this.setState({ compteur: this.state.compteur + 1 });
};
render() {
return (
<div>
<p>Compteur: {this.state.compteur}</p>
<button onClick={this.incrementer}>Incrémenter</button>
</div>
);
}
}
// Composant fonctionnel
type BoutonProps = {
onClick: () => void;
enfants: React.ReactNode;
};
const Bouton: React.FC<BoutonProps> = ({ onClick, enfants }) => (
<button onClick={onClick}>{enfants}</button>
);
Questions fréquentes
Quelle différence entre TypeScript et JavaScript ?
TypeScript est un langage à types statiques avec vérification à la compilation ; JavaScript est à types dynamiques.
Comment gérer les bibliothèques tierces sans définitions de types ?
Instalelz les paquets de types communautaires : npm install @types/nom-bibliotheque. Ou déclarez manuellement dans un fichier .d.ts avec declare module 'nom-bibliotheque';.
Comment migrer progressivement un projet JavaScript vers TypeScript ?
1. Renommez les fichiers en .ts et corrigez les erreurs de type. 2. Configurez allowJs: true dans tsconfig.json pour une compilation mixte.
Applications
Applications frontend
Props de composants avec sécurité de type
interface BoutonProps {
enfants: React.ReactNode;
onClick: () => void;
variante?: "primaire" | "secondaire";
desactive?: boolean;
}
const Bouton: React.FC<BoutonProps> = ({
enfants,
onClick,
variante = "primaire",
desactive = false
}) => {
return (
<button
className={`btn-${variante}`}
onClick={onClick}
disabled={desactive}
>
{enfants}
</button>
);
};
// Utilisation avec vérification des types
<Bouton variante="primaire" onClick={() => console.log("Cliqué")}>
Valider
</Bouton>
Gestion d'état (Redux + TypeScript)
// Définition des types d'action
type ActionCompteur =
| { type: 'INCREMENTER'; payload: number }
| { type: 'DECREMENTER'; payload: number };
// Définition du type d'état
interface EtatCompteur {
compteur: number;
}
// Fonction reducer
const reducerCompteur = (
etat: EtatCompteur = { compteur: 0 },
action: ActionCompteur
): EtatCompteur => {
switch (action.type) {
case 'INCREMENTER':
return { compteur: etat.compteur + action.payload };
case 'DECREMENTER':
return { compteur: etat.compteur - action.payload };
default:
return etat;
}
};
Applications backend
Routes Express avec sécurité de type
import express, { Request, Response } from 'express';
interface Personne {
id: number;
nom: string;
courriel: string;
}
const application = express();
application.use(express.json());
// GET /personnes/:id
application.get('/personnes/:id', (req: Request<{ id: string }>, res: Response<Personne>) => {
const personneId = parseInt(req.params.id);
const personne: Personne = { id: personneId, nom: "Alice", courriel: "alice@exemple.com" };
res.json(personne);
});
// POST /personnes
application.post('/personnes', (req: Request<{}, {}, Personne>, res: Response<{ succes: boolean }>) => {
const nouvellePersonne = req.body;
console.log("Création de personne:", nouvellePersonne);
res.json({ succes: true });
});
Opérations de base de données (TypeORM + TypeScript)
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm';
@Entity()
class Personne extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
nom: string;
@Column({ unique: true })
courriel: string;
static async chercherParNom(nom: string): Promise<Personne | undefined> {
return this.findOne({ where: { nom } });
}
}
// Utilisation
const personne = await Personne.chercherParNom("Alice");
Développement d'outils
Gestion de configuration avec sécurité de type
interface ConfigApp {
port: number;
bd: {
hote: string;
utilisateur: string;
motDePasse: string;
};
environnement: "developpement" | "production";
}
const chargerConfig = (): ConfigApp => {
return {
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
bd: {
hote: process.env.DB_HOST || "localhost",
utilisateur: process.env.DB_USER || "root",
motDePasse: process.env.DB_PASSWORD || "",
},
environnement: process.env.NODE_ENV === "production" ? "production" : "developpement",
};
};
const config = chargerConfig();
console.log(config.bd.hote);
Fonctions utilitaires génériques
// Type de réponse paginée
interface ReponsePaginee<T> {
donnees: T[];
page: number;
total: number;
}
// Fonction générique pour la pagination
function paginer<T>(elements: T[], page: number, taillePage: number): ReponsePaginee<T> {
const debut = (page - 1) * taillePage;
const fin = debut + taillePage;
return {
donnees: elements.slice(debut, fin),
page,
total: elements.length,
};
}
// Exemple d'utilisation
const utilisateurs = [{ id: 1, nom: "Alice" }, { id: 2, nom: "Bob" }];
const resultat = paginer(utilisateurs, 1, 1);
Opérations avancées sur les types
Garde de type
interface Chat {
type: "chat";
miauler: () => void;
}
interface Chien {
type: "chien";
aboyer: () => void;
}
type Animal = Chat | Chien;
function gererAnimal(animal: Animal) {
if (animal.type === "chat") {
animal.miauler();
} else {
animal.aboyer();
}
}
Types conditionnels et mappés
// Rendre toutes les propriétés optionnelles
type PartielTout<T> = {
[K in keyof T]?: T[K];
};
// Type conditionnel pour extraire le type de retour
type TypeRetour<T> = T extends (...args: any[]) => infer R ? R : never;
// Exemple
function multiplier(a: number, b: number): number { return a * b; }
type TypeRetourMultiplier = TypeRetour<typeof multiplier>; // number
Tests et débogage
Tests avec sécurité de type (Jest + TypeScript)
// Fonction utilitaire à tester
function somme(a: number, b: number): number {
return a + b;
}
describe('Fonction somme', () => {
it('additionne correctement deux nombres', () => {
expect(somme(2, 3)).toBe(5);
});
it('rejette les entrées non numériques', () => {
// @ts-expect-error - Test intentionnel de la sécurité des types
expect(somme("2", 3)).toThrow();
});
});
Solutions pour scénarios courants
Gérer les bibliothèques sans définitions de types
// Créer un fichier declarations.d.ts
declare module 'bibliotheque-non-typee' {
export function effectuerAction(config: any): void;
}
// Utilisation
import { effectuerAction } from 'bibliotheque-non-typee';
effectuerAction({ cle: "valeur" });
Objets avec propriétés dynamiques
interface ObjetDynamique {
[cle: string]: string | number;
}
const monObjet: ObjetDynamique = {
nom: "Alice",
age: 30,
// La ligne suivante provoquerait une erreur
// estAdmin: true
};
Ressources recommandées
- TypeScript Playground : pour expérimenter en ligne
- Documentation des types utilitaires
- DefinitelyTyped : dépôt de définitions de types tierces