frontend / typescript
Typage de librairies externes
Explication
Ce que vous allez apprendre
- Installer et utiliser un paquet
@types/nom-de-la-librairiedepuis DefinitelyTyped - Écrire un fichier
.d.tsavecdeclare modulepour une librairie sans types - Typer l'import de fichiers non-JavaScript (SVG, modules CSS) via une déclaration de module
- Déclarer une variable globale injectée par un script tiers avec
declare global - Étendre un type tiers déjà existant grâce à la module augmentation, sans le forker
- Comprendre pourquoi ces déclarations n'ont aucun effet sur le comportement réel du code
Dans quel contexte ?
Un développeur ajoute au projet une petite librairie de tracking analytics trouvée sur npm, ma-lib-tracking, qui n'a jamais été portée en TypeScript et ne fournit aucun @types associé. À l'import, TypeScript refuse de compiler : Could not find a declaration file for module 'ma-lib-tracking'. Deux solutions s'offrent alors : chercher si un paquet @types/ma-lib-tracking existe malgré tout côté communauté, ou écrire soi-même un petit fichier .d.ts de quelques lignes décrivant la forme de l'API exposée. Cette leçon couvre les deux cas, plus les variantes qu'on rencontre régulièrement (variables globales, imports non-JS, extension d'un type tiers).
Que se passe-t-il quand une librairie n'a pas de types ?
Toutes les bibliothèques JavaScript ne sont pas écrites en TypeScript, et certaines plus anciennes ne fournissent aucune information de type. Pour rester compatible avec l'écosystème JavaScript existant sans perdre les bénéfices de TypeScript, le langage propose plusieurs mécanismes pour décrire, séparément du code source, la forme d'une librairie externe.
DefinitelyTyped, le cas le plus courant
Pour la majorité des librairies populaires, quelqu'un a déjà écrit et publié ces types dans le paquet @types/nom-de-la-librairie : il suffit de l'installer pour bénéficier d'une expérience typée complète, sans écrire une seule ligne de déclaration soi-même.
| Situation | Solution |
|---|---|
| Librairie populaire, sans types intégrés | npm install -D @types/nom-de-la-librairie |
Petite librairie interne ou obscure, sans @types | Écrire soi-même un .d.ts avec declare module |
| Variable globale injectée par un script tiers | declare global { interface Window { ... } } |
| Import de fichiers non-JS (SVG, CSS modules) | declare module "*.svg" dans un .d.ts |
| Ajouter un champ à un type tiers déjà typé | Module augmentation (declare module "express") |
Prérequis
Cette leçon suppose que vous êtes à l'aise avec les interfaces et la syntaxe import/export de TypeScript : elle ne touche à aucune nouvelle syntaxe de typage, seulement à l'endroit où ces déclarations vivent (des fichiers .d.ts séparés du code exécuté).
Écrire ses propres déclarations
Quand aucun @types n'existe, on peut écrire un fichier .d.ts, un fichier de déclaration sans implémentation, qui décrit la forme attendue avec declare module. C'est aussi cette technique qui permet de typer l'import de fichiers non-JavaScript comme les SVG ou les modules CSS, ou de déclarer des variables globales injectées par un script tiers, comme un widget de suivi chargé via une balise script.
Étendre un type existant sans le modifier
La module augmentation (declare module "express") permet d'ajouter des propriétés à un type déjà défini par une librairie tierce — par exemple ajouter un champ utilisateurId à l'objet Request d'Express après qu'un middleware d'authentification l'ait injecté — sans avoir à modifier ni forker le code source de la librairie elle-même.
Piège fréquent
Un fichier .d.ts qui contient un export ou un import devient un module, et declare global doit alors être complété par un export {} explicite en fin de fichier pour rester reconnu comme tel — sans ce détail, TypeScript ignore silencieusement les déclarations globales qu'il contient.
Le fil conducteur
Cette leçon boucle une idée déjà croisée plusieurs fois dans ce cours : les types de TypeScript n'ont aucune existence à l'exécution, ils sont un outil de vérification pure. Décrire une librairie externe, c'est donc uniquement informer le compilateur, jamais modifier son comportement réel.
Commandes & code
Typage de librairies externes
// Cas 1 : une librairie JS sans types fournit un fichier .d.ts séparé (DefinitelyTyped)
// npm install -D @types/lodash
import _ from "lodash";
_.chunk([1, 2, 3, 4], 2); // types fournis par @types/lodash, aucun code à écrire
// Cas 2 : écrire ses propres déclarations pour une lib sans @types
// fichier : src/types/ma-lib-non-typee.d.ts
declare module "ma-lib-non-typee" {
export function initialiser(config: { cle: string }): void;
export function envoyer(evenement: string, donnees?: Record<string, unknown>): Promise<void>;
const version: string;
export default version;
}
// Utilisation ensuite comme une lib typée normale
import version, { initialiser, envoyer } from "ma-lib-non-typee";
// Cas 3 : déclarer des variables globales injectées par un script externe (CDN, widget tiers)
// fichier : src/types/globals.d.ts
declare global {
interface Window {
gtag: (commande: string, id: string, params?: Record<string, unknown>) => void;
__CONFIG__: {
apiUrl: string;
environnement: "dev" | "prod";
};
}
}
export {}; // requis pour que ce fichier soit traité comme un module
// Utilisation
window.gtag("event", "achat", { valeur: 42 });
console.log(window.__CONFIG__.apiUrl);
// Cas 4 : typer l'import d'assets non-JS (SVG en tant que composant, CSS modules)
// fichier : src/types/assets.d.ts
declare module "*.svg" {
const contenu: string;
export default contenu;
}
declare module "*.module.css" {
const classes: { readonly [nomClasse: string]: string };
export default classes;
}
// Cas 5 : élargir un module tiers déjà typé (module augmentation)
import "express";
declare module "express" {
interface Request {
utilisateurId?: string; // injectée par un middleware d'authentification
}
}
// Cas 6 : typer un module CommonJS "brut" en export = / import =
// fichier : legacy-lib.d.ts
declare module "legacy-lib" {
function run(config: unknown): void;
export = run;
}
import run = require("legacy-lib");Résumé
declare module "nom"type une librairie sans@typesdisponible sur npm.declare globalétend des interfaces globales (Window,globalThis) — ajouterexport {}.- La module augmentation (
declare module "express") enrichit un type tiers sans le forker.
Exercices pratiques
Mission : typer ma-lib-tracking sans types officiels
Objectif : Résoudre une erreur de compilation causée par l'absence de types pour une librairie tierce, en écrivant un fichier de déclaration adapté.
Contexte
Un développeur ajoute au projet ma-lib-tracking, une petite librairie npm sans version TypeScript ni paquet @types associé. À l'import, TypeScript refuse de compiler : Could not find a declaration file for module 'ma-lib-tracking'. Ta mission : résoudre cette erreur en écrivant les déclarations nécessaires.