Cours TypeScript Complet 🔷
Les 12 chapitres essentiels — du typage de base aux generics avancés, avec exemples et quiz
7. Classes et POO
2. Types primitifs et annotations
8. Generics
3. Objets, interfaces et types
9. Type Guards et Narrowing
4. Union, intersection et littéraux
10. Utility Types
5. Fonctions typées
11. Décorateurs et modules
6. Arrays, Tuples et Enums
12. Bonnes pratiques et configuration
Introduction et setup
TypeScript est un superset de JavaScript : tout code JS valide est du TS valide. TypeScript ajoute un système de types statique qui détecte les erreurs à la compilation, pas à l'exécution.
function add(a, b)
add(« 5 », 3); // « 53 » — pas d'erreur, mais un bug
// TypeScript — erreur détectée AVANT l'exécution
function add(a: number, b: number): number
add(« 5 », 3); // ❌ Erreur : « 5 » n'est pas un number
npm install -g typescript
# Initialiser un projet (crée tsconfig.json)
tsc –init
# Compiler un fichier .ts → .js
tsc app.ts
# Compiler tout le projet
tsc
# Mode watch (recompile automatiquement)
tsc –watch
# Exécuter directement (sans compiler) avec ts-node
npx ts-node app.ts
TS n'existe qu'à la compilation. Le navigateur et Node.js exécutent du JavaScript. TypeScript est transpilé en JS puis les types sont effacés (type erasure). Aucun impact sur les performances runtime.
Types primitifs et annotations
let nom: string = « Alice »;
let age: number = 25; // int et float = number
let actif: boolean = true;
let rien: null = null;
let indefini: undefined = undefined;
let gros: bigint = 100n;
let id: symbol = Symbol(« id »);
// Inférence de type — TS déduit automatiquement
let ville = « Paris »; // TS infère string
let score = 100; // TS infère number
const PI = 3.14; // TS infère 3.14 (type littéral !)
L'inférence est votre amie. N'annotez pas quand TS peut déduire le type : let x = 5; est meilleur que let x: number = 5;. Annotez les paramètres de fonctions et les cas ambigus.
| Type | Description | Usage |
|---|---|---|
| any | Désactive le type-checking | ❌ À éviter — ruine tout l'intérêt de TS |
| unknown | Valeur inconnue (sûr) | ✅ Alternative sûre à any — impose un check avant usage |
| void | Pas de retour | Fonctions qui ne retournent rien |
| never | Ne se produit jamais | Fonctions qui throw ou boucles infinies |
let x: any = « hello »;
x.whatever(); // Pas d'erreur → crash au runtime
// unknown — oblige à vérifier avant d'utiliser
let y: unknown = « hello »;
y.toUpperCase(); // ❌ Erreur — type inconnu
if (typeof y === « string »)
// never — pour les fonctions qui ne retournent JAMAIS
function erreur(msg: string): never
Objets, interfaces et types
interface User
// Type alias — peut typer n'importe quoi
type User = ;
// Utilisation
const alice: User = ;
alice.email = « a@b.com »; // ✅ OK
alice.id = 2; // ❌ readonly
interface :
• Objets et classes
• Peut être étendue (extends)
• Peut être « merged » (déclarations multiples)
• Recommandé par défaut pour les objets
type :
• Unions, intersections, tuples
• Types primitifs renommés
• Types utilitaires complexes
• Tout ce qui n'est pas un objet
interface Admin extends User
// Intersection de types
type Admin = User & ;
// Index signature — clés dynamiques
interface Dictionary
const scores: Dictionary = ;
type.Union, intersection et littéraux
let id: string | number;
id = « abc »; // ✅
id = 123; // ✅
id = true; // ❌
// TS ne permet que les méthodes communes
id.toString(); // ✅ Commun à string et number
id.toUpperCase(); // ❌ Seulement sur string
// Narrowing — vérifier le type pour accéder aux méthodes
if (typeof id === « string »)
let direction: « nord » | « sud » | « est » | « ouest »;
direction = « nord »; // ✅
direction = « haut »; // ❌
// Littéraux numériques
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
// Discriminated unions (pattern très puissant)
type Shape =
|
|
| ;
function area(s: Shape): number
Discriminated unions = le pattern le plus utile de TypeScript. Un champ commun (kind) permet à TS de savoir exactement quel type est utilisé dans chaque branche.
type HasName = ;
type HasAge = ;
type Person = HasName & HasAge;
const p: Person = ; // ✅ Les deux requis
Fonctions typées
function add(a: number, b: number): number
// Paramètre optionnel (?)
function saluer(nom: string, titre?: string): string
// Paramètre par défaut
function puissance(base: number, exp: number = 2): number
// Arrow function typée
const double = (x: number): number => x * 2;
// Type de fonction (signature)
type MathFn = (a: number, b: number) => number;
const add: MathFn = (a, b) => a + b;
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string
// Callback typé
function fetchData(url: string, callback: (data: string) => void): void
// Assertion de type (as)
const input = document.querySelector(« #email ») as HTMLInputElement;
input.value; // TS sait que c'est un input
Arrays, Tuples et Enums
const nombres: number[] = [1, 2, 3];
const noms: Array<string> = [« Alice », « Bob »];
// Array en lecture seule
const fixe: readonly number[] = [1, 2, 3];
fixe.push(4); // ❌ Erreur — readonly
let coord: [number, number] = [48.8, 2.3];
let entry: [string, number] = [« Alice », 25];
entry[0].toUpperCase(); // ✅ TS sait que c'est un string
entry[1].toFixed(2); // ✅ TS sait que c'est un number
// Tuple readonly (le plus sûr)
const rgb: readonly [number, number, number] = [255, 0, 128];
// Tuple avec labels (lisibilité)
type Range = [min: number, max: number];
enum Direction
// Enum string (recommandé — plus explicite)
enum Status
const s: Status = Status.Active;
// ✅ Alternative moderne : union de littéraux (pas de code JS généré)
type Status = « ACTIVE » | « INACTIVE » | « PENDING »;
Enums vs Union littérale : Les enums génèrent du code JavaScript. Les unions de littéraux sont effacées à la compilation (zero runtime cost). Beaucoup de projets modernes préfèrent les unions : type Status = « active » | « inactive ».
Classes et POO
// Raccourci : parameter properties
class User
Parameter properties : Ajouter public, private, protected ou readonly devant un paramètre du constructeur crée et assigne automatiquement la propriété. Énorme gain de boilerplate.
class Chien extends Animal implements Serializable
// Classe abstraite
abstract class Forme
this.nom et l'assigne automatiquement. Équivalent à this.nom = nom; dans le corps.Generics
function premier(arr: any[]): any
// Avec generic — le type est préservé
function premier<T>(arr: T[]): T
const n = premier([1, 2, 3]); // n: number ✅
const s = premier([« a », « b »]); // s: string ✅
// TS infère T automatiquement d'après l'argument
function plusLong<T extends >(a: T, b: T): T
plusLong(« hello », « hi »); // « hello »
plusLong([1,2,3], [1]); // [1,2,3]
plusLong(10, 20); // ❌ number n'a pas .length
// Plusieurs paramètres génériques
function paire<K, V>(key: K, value: V): [K, V]
// keyof — clés d'un type
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K]
const user = ;
getProperty(user, « nom »); // string ✅
getProperty(user, « xyz »); // ❌ « xyz » n'est pas une clé de user
interface ApiResponse<T>
const res: ApiResponse<User[]> = ;
// Classe générique
class Stack<T>
const stack = new Stack<number>();
stack.push(42);
stack.push(« hello »); // ❌ Pas un number
length de type number (string, array, etc.).Type Guards et Narrowing
// instanceof guard
function handleError(err: Error | string)
// « in » guard
type Fish = ;
type Bird = ;
function move(animal: Fish | Bird)
function isString(value: unknown): value is string
function demo(x: unknown)
// Discriminated union narrowing (le plus courant)
type Result<T> =
|
| ;
function handle<T>(result: Result<T>)
Utility Types
function updateUser(id: number, updates: Partial<User>): void
updateUser(1, ); // ✅ Seulement age
// Record — créer un dictionnaire typé
type Scores = Record<string, number>;
const scores: Scores = ;
// Pick + Omit — créer des sous-types
type UserPreview = Pick<User, « nom » | « role »>;
type UserWithoutEmail = Omit<User, « email »>;
// Readonly — objet immuable
const config: Readonly<User> = ;
config.age = 26; // ❌ readonly
Pick = . Omit = .Décorateurs et modules
export interface User
export function greet(u: User): string
export default class App
// import
import App from « ./app »; // default
import from « ./app »; // named
import type from « ./app »; // type-only (effacé au JS)
// Déclarations de types pour des libs JS (.d.ts)
// npm install @types/lodash — types pour lodash
// DefinitelyTyped : repo communautaire de types
import type : Quand vous importez uniquement un type/interface, utilisez import type. Cela garantit que l'import est effacé dans le JS final (zero runtime cost).
// Activer : « experimentalDecorators »: true dans tsconfig
function Log(target: any, key: string, desc: PropertyDescriptor)
class Calculator
// « Appel add avec [2, 3] » est loggé automatiquement
Bonnes pratiques et configuration
strict: true est NON-NÉGOCIABLE. Il active toutes les vérifications strictes (strictNullChecks, noImplicitAny, etc.). Sans lui, TypeScript perd la moitié de son utilité.
✅ À FAIRE
• strict: true toujours
• Utiliser l'inférence quand possible
• unknown au lieu de any
• interface pour les objets
• Discriminated unions pour les variants
• Readonly par défaut
• import type pour les types
• Types stricts dans les API publiques
❌ À ÉVITER
• any (utiliser unknown)
• as abusif (assertion de type)
• ! non-null assertion sans vérifier
• @ts-ignore (masque les erreurs)
• Enums numériques (préférer string ou unions)
• Types trop complexes (simplicité d'abord)
• Surtyper (n'annotez pas l'évident)
• Function et Object comme types
type Optional<T> = ;
// Template literal type
type Event = « click » | « focus » | « blur »;
type Handler = `on$`;
// « onClick » | « onFocus » | « onBlur »
// Conditional type
type IsString<T> = T extends string ? « oui » : « non »;
type A = IsString<« hello »>; // « oui »
type B = IsString<42>; // « non »
// infer — extraire un type dans un conditionnel
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type X = UnwrapPromise<Promise<string>>; // string
Cours TypeScript Complet — Des types de base aux Generics avancés
Référence : TypeScript Docs | TS Playground

