Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🔷
Avance 25 min TypeScript

Generics en TypeScript

Pourquoi Generics en TypeScript ?

Le generics est un élément essentiel de la programmation moderne, offrant une flexibilité accrue dans la conception et l'utilisation des types. En tant que développeur senior TypeScript avec 10+ ans d'expérience, vous devriez comprendre qu'un bon usage des generics peut grandement améliorer la lisibilité du code, les performances et la robustesse de vos applications.

Dans le monde réel, imaginez un scénario où vous travaillez sur une application qui doit gérer différents types de données. Sans generics, vous devriez peut-être écrire des fonctions pour chaque type de données distincte, ce qui rend votre code plus complexe et difficile à maintenir. Avec les generics, vous pouvez créer des composants réutilisables qui peuvent travailler avec n'importe quel type de données.

Prerequis

  • Connaissances en TypeScript (version 2.3 ou supérieure recommandée)
  • Un environnement de développement Node.js
  • L'IDE de votre choix (VSCode, WebStorm, etc.)

Installez les outils suivants :

npm install -g typescript
tsc --init

Concepts fondamentaux

1. Définition des Generics

Les generics permettent de créer des composants qui peuvent travailler avec un type non spécifié. Ils sont déclarés entre < >.

function identity<T>(arg: T): T {
    return arg;
}

2. Utilisation d'un Generic en Interface

Vous pouvez également utiliser des generics dans les interfaces pour créer des types flexibles.

interface Box<T> {
    value: T;
}

const numberBox: Box<number> = { value: 10 };
const stringBox: Box<string> = { value: "Hello" };

3. Generics avec Classes

Les classes peuvent également utiliser des generics pour offrir une flexibilité accrue.

class GenericArray<T> {
    private items: T[] = [];

    add(item: T) {
        this.items.push(item);
    }

    get(index: number): T {
        return this.items[index];
    }
}

const numbers = new GenericArray<number>();
numbers.add(10);
numbers.add(20);

console.log(numbers.get(0)); // Output: 10

4. Generics avec Fonctions de Callbacks

Les generics peuvent être utilisés dans les fonctions qui acceptent des callbacks.

function map<T, U>(array: T[], callback: (item: T) => U): U[] {
    const result: U[] = [];
    for (const item of array) {
        result.push(callback(item));
    }
    return result;
}

const numbers = [1, 2, 3];
const doubledNumbers = map(numbers, (num) => num * 2);
console.log(doubledNumbers); // Output: [2, 4, 6]

Mise en pratique : Projet fil rouge

Nous allons construire un gestionnaire de tâches simple pour illustrer l'utilisation des generics.

Étape 1 : Création du projet

Créez un nouveau dossier et initialisez le projet :

mkdir task-manager
cd task-manager
npm init -y

Installez les dépendances nécessaires :

npm install typescript @types/node --save-dev
npx tsc --init

Étape 2 : Configuration du fichier tsconfig.json

Modifiez le fichier tsconfig.json pour configurer la compilation TypeScript :

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "outDir": "./dist"
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules"]
}

Étape 3 : Création des fichiers

Créez un dossier src et ajoutez les fichiers suivants :

  • task.ts
  • task-manager.ts
  • index.ts

task.ts

// task.ts
export interface Task {
    id: number;
    title: string;
    completed: boolean;
}

task-manager.ts

// task-manager.ts
import { Task } from './task';

export class TaskManager<T extends Task> {
    private tasks: T[] = [];

    addTask(task: T) {
        this.tasks.push(task);
    }

    getTasks(): T[] {
        return this.tasks;
    }
}

index.ts

// index.ts
import { Task, TaskManager } from './task-manager';

const task1: Task = { id: 1, title: 'Task 1', completed: false };
const task2: Task = { id: 2, title: 'Task 2', completed: true };

const taskManager = new TaskManager<Task>();
taskManager.addTask(task1);
taskManager.addTask(task2);

console.log(taskManager.getTasks());

Compilation et exécution

Compiler le code TypeScript :

npx tsc

Exécuter le fichier compilé :

node dist/index.js

Erreurs fréquentes et debugging

1. Erreur de type non spécifié

function printArray(array) {
    array.forEach(item => console.log(item));
}

const numbers = [1, 2, 3];
printArray(numbers); // Pas d'erreur compile-time

Correction :

function printArray<T>(array: T[]) {
    array.forEach(item => console.log(item));
}

const numbers = [1, 2, 3];
printArray<number>(numbers);

2. Erreur d'incompatibilité de type

interface Box<T> {
    value: T;
}

function swap(a: Box<any>, b: Box<any>) {
    const temp = a.value;
    a.value = b.value;
    b.value = temp;
}

const box1 = { value: 10 };
const box2 = { value: "Hello" };

swap(box1, box2); // Pas d'erreur compile-time

Correction :

interface Box<T> {
    value: T;
}

function swap<T>(a: Box<T>, b: Box<T>) {
    const temp = a.value;
    a.value = b.value;
    b.value = temp;
}

const box1: Box<number> = { value: 10 };
const box2: Box<string> = { value: "Hello" };

swap(box1, box2); // Erreur compile-time

3. Erreur de type non spécifique

function findFirst<T>(array: T[], predicate: (item: T) => boolean): T {
    for (const item of array) {
        if (predicate(item)) {
            return item;
        }
    }
}

const numbers = [1, 2, 3];
const firstEven = findFirst(numbers, num => num % 2 === 0);
console.log(firstEven); // Pas d'erreur compile-time

Correction :

function findFirst<T>(array: T[], predicate: (item: T) => boolean): T | undefined {
    for (const item of array) {
        if (predicate(item)) {
            return item;
        }
    }
}

const numbers = [1, 2, 3];
const firstEven = findFirst(numbers, num => num % 2 === 0);
console.log(firstEven); // Output: undefined

Pour aller plus loin

1. Les generics avec des tuples

Les tuples sont des structures de données qui permettent de stocker un nombre fixe d'éléments de types différents. Vous pouvez utiliser des generics pour créer des fonctions qui travaillent sur des tuples.

function swapTuple<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

const tuple = [1, "Hello"];
const swappedTuple = swapTuple(tuple);
console.log(swappedTuple); // Output: ["Hello", 1]

2. Les generics avec des classes génériques

Les classes génériques peuvent être utilisées pour créer des structures de données réutilisables qui peuvent travailler avec n'importe quel type de données.

class PriorityQueue<T> {
    private items: { priority: number, item: T }[] = [];

    enqueue(item: T, priority: number) {
        this.items.push({ item, priority });
        this.items.sort((a, b) => a.priority - b.priority);
    }

    dequeue(): T | undefined {
        if (this.items.length > 0) {
            return this.items.shift()?.item;
        }
        return undefined;
    }
}

const pq = new PriorityQueue<number>();
pq.enqueue(1, 2);
pq.enqueue(3, 1);

console.log(pq.dequeue()); // Output: 3

3. Les generics avec des interfaces génériques

Les interfaces génériques peuvent être utilisées pour définir des types flexibles qui peuvent travailler avec n'importe quel type de données.

interface Cache<T> {
    set(key: string, value: T): void;
    get(key: string): T | undefined;
}

class InMemoryCache<T> implements Cache<T> {
    private store: { [key: string]: T } = {};

    set(key: string, value: T) {
        this.store[key] = value;
    }

    get(key: string): T | undefined {
        return this.store[key];
    }
}

const cache = new InMemoryCache<number>();
cache.set("one", 1);
cache.set("two", 2);

console.log(cache.get("one")); // Output: 1

Défi pratique : Créer une API de blog

Créez une API simple en utilisant Express.js et TypeScript pour gérer les articles d'un blog. Utilisez des generics pour créer un système générique de gestion des ressources.

// server.ts
import express from 'express';
import { TaskManager } from './task-manager';

const app = express();
app.use(express.json());

interface Article {
    id: number;
    title: string;
    content: string;
}

const articleManager = new TaskManager<Article>();

app.post('/articles', (req, res) => {
    const article: Article = req.body;
    article.id = Date.now();
    articleManager.addTask(article);
    res.status(201).json(article);
});

app.get('/articles', (req, res) => {
    const articles = articleManager.getTasks();
    res.json(articles);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Installation des dépendances

npm install express --save

Compilation et exécution

npx tsc
node dist/server.js

Ce tutoriel vous a permis de comprendre les bases et les concepts avancés des generics en TypeScript. Vous avez également créé un projet fil rouge pour mettre en pratique vos connaissances. Continuez à explorer les autres fonctionnalités de TypeScript pour approfondir votre compréhension du langage.

Besoin d'aide sur TypeScript ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Qu'est-ce que les generics en TypeScript?
Les generics en TypeScript permettent de créer des composants réutilisables qui peuvent travailler avec différents types de données sans perdre la vérification de type au moment de la compilation.
Comment utiliser les generics dans une fonction?
Pour utiliser un generic dans une fonction, vous déclarez une variable générique entre chevrons angle (<>), par exemple . Ensuite, vous utilisez cette variable générique comme type pour les paramètres et le retour de la fonction.
Quelle est l'avantage des generics?
Les generics offrent plusieurs avantages : ils permettent une meilleure réutilisabilité du code, évitent les erreurs de type à la runtime grâce à la vérification de type au moment de la compilation, et rendent le code plus évident à lire en exposant clairement les types d'entrée et de sortie.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.