Cours TypeScript Complet 🔷

Les 12 chapitres essentiels — du typage de base aux generics avancés, avec exemples et quiz

12
Chapitres
100+
Exemples de code
v5.x
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et setup

🔷 TypeScript = JavaScript + Types

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.

// JavaScript — l'erreur n'apparaît qu'au runtime
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

⚙️ Installation et compilation
# Installer TypeScript
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.

🧠 Quiz
TypeScript s'exécute-t-il dans le navigateur ?
Non. TypeScript est compilé en JavaScript. Le navigateur exécute le JS résultant. Les types sont effacés à la compilation.

CHAPITRE 02

Types primitifs et annotations

📦 Les types de base
// Annotation de type : variable: type = valeur
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.

⚡ any, unknown, void, never
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
// any — aucune vérification (dangereux)
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

🧠 Quiz
Différence entre any et unknown ?
any = tout est permis (pas de vérification). unknown = la valeur peut être n'importe quoi mais vous devez vérifier le type avant de l'utiliser. Préférez toujours unknown.

CHAPITRE 03

Objets, interfaces et types

📋 interface vs type
// Interface — décrit la forme d'un objet
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 vs type — quand utiliser quoi ?

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

// Extension d'interface
interface Admin extends User

// Intersection de types
type Admin = User & ;

// Index signature — clés dynamiques
interface Dictionary
const scores: Dictionary = ;

🧠 Quiz
Peut-on utiliser interface pour typer une union « string | number » ?
Non. Les interfaces ne décrivent que des objets. Pour les unions, intersections, tuples et types primitifs, utilisez type.

CHAPITRE 04

Union, intersection et littéraux

🔀 Union types (|)
// La valeur peut être l'un OU l'autre
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 »)

📌 Types littéraux
// Un type littéral = une valeur précise
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.

🔗 Intersection (&)
// Combine plusieurs types — l'objet doit avoir TOUT
type HasName = ;
type HasAge = ;
type Person = HasName & HasAge;

const p: Person = ; // ✅ Les deux requis

CHAPITRE 05

Fonctions typées

🔧 Typer les fonctions
// Paramètres et retour typés
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;

⚡ Surcharge (overloads) et callbacks
// Surcharge de fonctions
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

🧠 Quiz
Faut-il toujours annoter le type de retour ?
Pas toujours. TS infère le retour automatiquement. Annotez le retour pour les fonctions exportées (API publique) et quand l'inférence pourrait être ambiguë.

CHAPITRE 06

Arrays, Tuples et Enums

📋 Arrays typés
// Deux syntaxes (identiques)
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

📐 Tuples
// Tuple = array avec types fixes par position
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];

🏷️ Enums
// Enum numérique (auto-incrémenté)
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 ».

🧠 Quiz
Différence entre un array et un tuple en TypeScript ?
Array = longueur variable, tous les éléments du même type. Tuple = longueur fixe, chaque position a son propre type.

CHAPITRE 07

Classes et POO

🏛️ Classes TypeScript
class Animal

// 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.

🔗 Héritage et implements
interface Serializable

class Chien extends Animal implements Serializable

// Classe abstraite
abstract class Forme

🧠 Quiz
Que fait « public nom: string » dans un constructeur ?
C'est un parameter property : il déclare la propriété this.nom et l'assigne automatiquement. Équivalent à this.nom = nom; dans le corps.

CHAPITRE 08

Generics

🔄 Le problème que résolvent les generics
// Sans generic — on perd l'information de type
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

⚡ Generics avancés
// Contrainte (extends) — T doit avoir une propriété length
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

📦 Interfaces et classes génériques
// Interface générique
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

🧠 Quiz
Que signifie ?
C'est une contrainte générique : T peut être n'importe quel type à condition qu'il possède une propriété length de type number (string, array, etc.).

CHAPITRE 09

Type Guards et Narrowing

🔍 Narrowing = réduire un type union
function process(value: string | number)

// instanceof guard
function handleError(err: Error | string)

// « in » guard
type Fish = ;
type Bird = ;

function move(animal: Fish | Bird)

🛡️ Custom type guard (is)
// Predicate function — « value is Type »
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>)

CHAPITRE 10

Utility Types

🧰 Les Utility Types essentiels
interface User
Utility Type Résultat Usage
Partial Toutes les propriétés optionnelles Mises à jour partielles
Required Toutes les propriétés obligatoires Forcer la complétude
Readonly Toutes les propriétés en lecture seule Objets immuables
Pick Garde seulement nom et age Sous-ensemble de propriétés
Omit Toutes sauf email Exclure des propriétés
Record Dictionnaires
ReturnType Type de retour d'une fonction Extraire le type de retour
Parameters Tuple des paramètres Extraire les types de params
Exclude B Retirer d'une union
Extract A Garder d'une union
NonNullable T Retirer null et undefined
Awaited> T Déballer une Promise
⚡ Exemples pratiques
// Partial — mise à jour partielle
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

🧠 Quiz
Différence entre Pick et Omit ?
Pick garde les propriétés listées. Omit retire les propriétés listées. Pick = . Omit = .

CHAPITRE 11

Décorateurs et modules

📦 Modules TypeScript
// export — rendre disponible
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).

🎭 Décorateurs (TypeScript 5+)
// Un décorateur = fonction qui modifie une classe/méthode
// Activer : « experimentalDecorators »: true dans tsconfig

function Log(target: any, key: string, desc: PropertyDescriptor)

class Calculator
// « Appel add avec [2, 3] » est loggé automatiquement

CHAPITRE 12

Bonnes pratiques et configuration

⚙️ tsconfig.json essentiel

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é.

✅ Bonnes pratiques récap

✅ À 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

🔧 Mapped Types et Template Literal Types (avancé)
// Mapped type — transformer toutes les propriétés
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

🧠 Quiz
Pourquoi strict: true est-il indispensable ?
Il active strictNullChecks (null/undefined vérifiés), noImplicitAny (pas de any implicite), et d'autres checks. Sans lui, TS ne détecte pas les erreurs les plus courantes (null access, types ambigus).
Que fait Partial ?
Il rend toutes les propriétés de User optionnelles (ajoute ? à chaque propriété). Utile pour les fonctions de mise à jour partielle.

Cours TypeScript Complet — Des types de base aux Generics avancés

Référence : TypeScript Docs | TS Playground