Erreur typique de setTimeout en TypeScript : window vs Node.js

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Ce n’est pas le bon setTimeout » : comment TypeScript rend les développeurs fous

\n

Avez-vous déjà écrit un simple timer en TypeScript, et soudainement l’éditeur de code commençait à tout souligner en rouge ? Une situation familière : vous importez setTimeout, vous écrivez const timer = setTimeout(...), et TypeScript affiche une erreur ou suggère un type incompréhensible. Quel est le problème ? Il s’avère qu’il existe deux versions de setTimeout — une pour le navigateur (window), l’autre pour le serveur (Node.js). Et TypeScript, étant perfectionniste, nous oblige à choisir.

\n

Ce sujet est l’un des plus populaires sur Stack Overflow. Des milliers de développeurs tombent dans le même piège. Mais nous ne nous contenterons pas de copier la réponse. Nous allons comprendre la racine du problème, voir comment fonctionne le typage, et apprendre à écrire un code qui ne se cassera ni dans le navigateur ni dans Node.js.

\n\n

Pourquoi deux versions de setTimeout ?

\n

Historiquement, JavaScript vit dans deux mondes : côté client (navigateur) et côté serveur (Node.js). Dans le navigateur, setTimeout est une méthode de l’objet global window. Dans Node.js, c’est une fonction du module global timers. Elles font la même chose, mais leurs signatures de types diffèrent.

\n

Voici les différences clés :

\n
    \n
  • Valeur de retour : dans le navigateur, window.setTimeout retourne un number (identifiant du timer). Dans Node.js, il retourne un objet NodeJS.Timeout.
  • \n
  • Contexte : dans le navigateur, this à l’intérieur du callback est window (en mode non strict). Dans Node.js, c’est l’objet timer.
  • \n
  • Typage : TypeScript fournit différentes déclarations pour différents environnements. Si vous écrivez du code universel (application isomorphe), un conflit apparaît.
  • \n
\n\n

À quoi ressemble le problème dans le code

\n

Imaginez que vous développez une bibliothèque qui doit fonctionner à la fois côté client et côté serveur. Vous écrivez ce code :

\n
// utils.ts\nexport function delay(ms: number): Promise<void> {\n    return new Promise(resolve => {\n        const timer = setTimeout(resolve, ms);\n        // Quel est le type de timer ? number ou NodeJS.Timeout ?\n    });\n}
\n

TypeScript verra que setTimeout peut être de deux types et affichera une erreur : "Type 'Timeout' is not assignable to type 'number'." Ou l’inverse, selon les paramètres de tsconfig.json et les types inclus.

\n

Pour être plus clair, voici à quoi ressemblent les types si on les examine via ReturnType :

\n
// Dans le navigateur (lib.dom.d.ts)\ntype BrowserTimer = ReturnType<typeof window.setTimeout>; // number\n\n// Dans Node.js (@types/node)\ntype NodeTimer = ReturnType<typeof setTimeout>; // NodeJS.Timeout
\n\n

Solutions correctes pour différents scénarios

\n

Examinons quatre approches. Choisissez celle qui convient à votre projet.

\n\n

1. Si vous écrivez uniquement pour le navigateur

\n

Le cas le plus simple. Indiquez simplement le type number et utilisez window.setTimeout explicitement :

\n
// browser-timer.ts\nconst timerId: number = window.setTimeout(() => {\n    console.log('Une seconde s’est écoulée');\n}, 1000);\n\n// Pour nettoyer\nwindow.clearTimeout(timerId);
\n

Pourquoi cela fonctionne-t-il ? Dans l’environnement navigateur, window.setTimeout retourne toujours un nombre. TypeScript le sait si vous avez inclus lib: ["dom"].

\n\n

2. Si vous écrivez uniquement pour Node.js

\n

Ici, le type est NodeJS.Timeout. Utilisez-le directement :

\n
// node-timer.ts\nimport { setTimeout } from 'timers'; // import explicite pour plus de clarté\n\nconst timer: NodeJS.Timeout = setTimeout(() => {\n    console.log('Le timer a été déclenché');\n}, 2000);\n\nclearTimeout(timer); // clearTimeout accepte NodeJS.Timeout
\n

Important : Dans Node.js, clearTimeout accepte également No

Blogs

Book Recommendations