Comment corriger les erreurs TypeScript liées aux nouvelles API des navigateurs
Corrigez les erreurs TypeScript liées aux nouvelles API du navigateur avec modern-web-types ou une déclaration personnalisée. Comprenez le retard des types DOM et vérifiez la prise en charge à l’exécution.
TypeScript signale « Property does not exist on type » pour certaines nouvelles API des navigateurs, car ses types DOM intégrés n’incluent que les API prises en charge par au moins deux moteurs de navigateur. Pour corriger ce problème, vous pouvez substituer une bibliothèque DOM plus complète ou déclarer vous-même le membre manquant.
Vous appelez une API documentée sur MDN et déjà exécutée par Chrome, et le compilateur vous répond qu’elle n’existe pas. La typer en any fait disparaître le soulignement rouge, mais vous perdez la vérification de types sur ce code.
Cet article explique pourquoi les types DOM de TypeScript sont en retard sur les navigateurs. Il présente ensuite deux corrections sûres du point de vue du typage, ainsi que la vérification à l’exécution que vous devez conserver dans les deux cas.
Points clés
- Le fichier lib.dom.d.ts de TypeScript est généré par microsoft/TypeScript-DOM-lib-generator, qui exclut toute API prise en charge par moins de deux moteurs de navigateur majeurs.
- modern-web-types abaisse ce seuil à un seul moteur stable. Installez-le avec
npm install --save-dev @typescript/lib-dom@npm:modern-web-types. - À partir de TypeScript 6, le paquet n’est pris en compte qu’avec
"libReplacement": truedans compilerOptions. De TypeScript 4.5 à 5.x, aucun paramètre supplémentaire n’est nécessaire. - Pour ajouter vous-même un membre manquant, utilisez
interface Windowdans un fichier.d.tsglobal. Un aliastypene fusionnera pas. - Disposer des types d’une API ne signifie pas que le navigateur la prend en charge. Les API disponibles sur un seul moteur nécessitent donc toujours une vérification comme
'fetchLater' in window.
Que signifie « Property does not exist on type » ?
L’erreur TypeScript TS2339, « Property does not exist on type », apparaît lorsque votre code appelle une véritable API du navigateur que les types DOM fournis avec TypeScript ne déclarent pas. Votre propre code n’a pas ajouté de variable globale comme window.myThing. Le navigateur fournit bien cette méthode, mais les types de TypeScript l’ignorent.
window.fetchLater('/analytics', { method: 'POST', activateAfter: 60_000 });
// error TS2339: Property 'fetchLater' does not exist on type 'Window & typeof globalThis'.
fetchLater() met en file d’attente une requête que le navigateur envoie lorsque l’utilisateur quitte la page (déchargement ou passage dans le bfcache). La requête part aussi à l’expiration du délai facultatif activateAfter, si celui-ci survient en premier. L’annonce de modern-web-types par Philip Walton cite également les transitions de vue limitées à un élément et la propriété scripts de Long Animation Frame comme exemples d’API touchées par la même erreur.
Pourquoi les types DOM de TypeScript sont-ils en retard sur les navigateurs ?
Ce retard est volontaire. Le README de TypeScript-DOM-lib-generator fixe le critère d’inclusion à deux moteurs majeurs ou plus parmi Gecko (Firefox), Blink (Chrome/Edge) et WebKit (Safari). Ainsi, seules les API bénéficiant d’un réel consensus entre éditeurs sont retenues. Tous les navigateurs basés sur Chromium reposent sur Blink : ils comptent donc tous comme un seul moteur.
Concrètement, une API livrée uniquement dans Blink peut fonctionner dans Chrome, Edge et tous les autres navigateurs Chromium, tandis que TypeScript affirmera toujours qu’elle n’existe pas. L’entrée web-features consacrée à fetchLater ne mentionne que des navigateurs basés sur Chromium, si bien que l’API ne satisfait pas la règle des deux moteurs.
L’erreur « Property does not exist on type » reflète la politique d’inclusion de TypeScript. Elle ne vous indique pas si le navigateur qui exécute votre code prend en charge l’API.
Correction 1 : remplacer lib.dom par modern-web-types
modern-web-types est un paquet de Philip Walton qui remplace les bibliothèques DOM et WebWorker de TypeScript sans autre modification de votre code. Comme l’explique son article d’annonce, il est construit avec le générateur de TypeScript et les données de w3c/webref. Il inclut cependant toute API livrée dans au moins un moteur de navigateur stable.
Installez-le sous l’alias @typescript/lib-dom. C’est le nom que TypeScript recherche lorsqu’il résout sa bibliothèque DOM :
npm install --save-dev @typescript/lib-dom@npm:modern-web-types
À partir de TypeScript 6, installer l’alias ne suffit pas : vous devez aussi activer explicitement le remplacement de lib :
{
"compilerOptions": {
"libReplacement": true
}
}
De TypeScript 4.5 à 5.x, omettez ce paramètre. TypeScript 4.5 a introduit la résolution des paquets @typescript/lib-* et l’appliquait automatiquement. TypeScript 5.8 l’a placée derrière l’option libReplacement, activée par défaut, puis TypeScript 6 l’a désactivée. La pull request de modern-web-types recommandant le remplacement de lib détaille cette évolution. Selon l’annonce de TypeScript 7.0, la version 7.0 reprend les valeurs par défaut de la 6.0, dont libReplacement: false. L’option est donc également nécessaire en 7.0.
Projets avec workers
Le code des workers utilise un point d’entrée distinct, ajouté via types. N’indiquez pas "WebWorker" dans lib, car ce point d’entrée le remplace :
{
"compilerOptions": {
"types": ["modern-web-types/webworker"],
"lib": ["ESNext"]
}
}
Sous TypeScript 5.x, le simple fait de définir types empêche TypeScript d’inclure automatiquement tous les paquets @types/* présents dans node_modules. À partir de TypeScript 6, types vaut déjà [] par défaut, donc rien n’est inclus automatiquement dans tous les cas. Si vous dépendez de paquets comme @types/node, ajoutez-les au même tableau. Le README liste aussi les points d’entrée serviceworker, sharedworker et audioworklet.
Correction 2 : déclarer l’API manquante par fusion d’interfaces
Si vous n’avez besoin que d’une seule API, un petit fichier de déclaration suffit. Déclarez une interface portant le même nom que l’interface intégrée, et TypeScript fusionne les déclarations. Il doit impérativement s’agir d’une interface. Un alias type Window = { ... } échoue avec une erreur d’identifiant dupliqué, car les alias de type ne fusionnent pas.
La signature ci-dessous suit MDN. fetchLater() accepte les mêmes arguments que fetch(), plus une option activateAfter. Elle renvoie un FetchLaterResult dont le booléen activated indique si la requête est déjà partie.
// src/types/fetch-later.d.ts
interface DeferredRequestInit extends RequestInit {
activateAfter?: number;
}
interface FetchLaterResult {
readonly activated: boolean;
}
interface Window {
fetchLater(input: RequestInfo | URL, init?: DeferredRequestInit): FetchLaterResult;
}
Cela fonctionne sans enveloppe declare global, car un fichier .d.ts sans import ni export de premier niveau est un script global. Trois règles garantissent son bon fonctionnement :
- Ne transformez pas le fichier en module. Un
importou unexportde premier niveau en fait un module. Les interfaces ne fusionnent alors plus globalement, etdeclare globalredevient nécessaire. - Vérifiez que le tsconfig le prend en compte. Le fichier doit correspondre à vos motifs
include. - Supprimez-le lorsque TypeScript intègre l’API. Dès que lib.dom déclare les mêmes membres, vos copies deviennent au mieux redondantes, au pire sources de conflits.
Cette déclaration type uniquement window.fetchLater(...). Appeler directement la fonction globale fetchLater(...) nécessite en plus une déclaration de fonction globale. Utilisez donc window.fetchLater dans votre code.
La détection de fonctionnalités reste-t-elle nécessaire ?
Oui. Les types TypeScript décrivent la forme d’une API, pas les navigateurs qui la proposent. Les deux corrections font accepter fetchLater au compilateur, mais les navigateurs qui ne l’implémentent pas lèveront tout de même une erreur à l’exécution. Protégez donc l’appel :
if ('fetchLater' in window) {
try {
window.fetchLater('/analytics', { method: 'POST', activateAfter: 60_000 });
} catch {
// quota exceeded: fall back or drop the beacon
}
} else {
// fall back, e.g. send the data immediately
}
Le bloc try est présent parce que MDN recommande d’intercepter QuotaExceededError autour de presque tous les appels à fetchLater(). Traitez les API disponibles sur un seul moteur comme de l’amélioration progressive. Vérifiez aussi leur prise en charge par rapport à vos navigateurs cibles sur la page MDN de fetchLater.
Quelle correction choisir ?
| Approche | Mise en place | Couverture | Quand TypeScript rattrape son retard |
|---|---|---|---|
| modern-web-types | Une installation, une option à partir de TS 6 | Toutes les API disponibles sur un seul moteur | Rien à supprimer |
.d.ts écrit à la main | Un petit fichier par API | Uniquement ce que vous déclarez | Supprimer le fichier |
as any / @ts-ignore | Aucune | Aucune | Masque indéfiniment les vraies erreurs |
Optez pour modern-web-types si votre projet utilise plusieurs nouvelles API ou adopte régulièrement des fonctionnalités avant leur arrivée dans d’autres moteurs. Préférez un .d.ts écrit à la main si vous n’avez besoin que d’un ou deux membres et ne souhaitez pas remplacer toute la bibliothèque DOM. Évitez as any et @ts-ignore : ils désactivent la vérification sur toute l’expression ou la ligne et masqueront plus tard de véritables erreurs. Une configuration ESLint pour TypeScript avec les règles @typescript-eslint/no-explicit-any et @typescript-eslint/ban-ts-comment empêche l’accumulation de ces contournements.
Conclusion
L’erreur TypeScript « Property does not exist on type » sur les nouvelles API des navigateurs découle de la règle des deux moteurs de TypeScript. Elle ne provient ni d’une erreur dans votre code ni d’une entrée "dom" manquante. Remplacez lib.dom par modern-web-types si vous utilisez régulièrement de nouvelles API, ou fusionnez une interface unique dans Window pour un besoin ponctuel. Dans les deux cas, conservez une vérification à l’exécution autour de chaque appel. Commencez par vérifier votre version de TypeScript : en version 6 ou ultérieure, ajoutez "libReplacement": true lors de l’installation du paquet.
FAQ
Installer @types/web corrige-t-il les types manquants des nouvelles API des navigateurs ?
Uniquement pour les API qui satisfont déjà la règle des deux moteurs. @types/web est la même bibliothèque DOM que celle fournie avec TypeScript. Elle est générée par microsoft/TypeScript-DOM-lib-generator et publiée sur npm, ce qui lui permet de livrer des types DOM plus récents avant la prochaine version de TypeScript. Elle applique toutefois la même politique d'inclusion : une API réservée à Chromium comme fetchLater reste donc absente. Elle s'installe via le même alias @typescript/lib-dom à partir de TypeScript 4.5 et, à partir de TypeScript 6, nécessite aussi libReplacement à true.
Ajouter DOM ou ESNext à l'option lib corrige-t-il l'erreur ?
Non. L'option lib sert uniquement à choisir quels fichiers de déclaration fournis avec TypeScript sont chargés. Le fichier DOM fourni est généré selon la règle des deux moteurs. Ajouter 'DOM', 'DOM.Iterable' ou 'ESNext' ne peut donc pas ajouter une API que ce fichier exclut. ESNext ajoute des fonctionnalités du langage JavaScript, pas des API de navigateur. Pour corriger l'erreur, remplacez la bibliothèque DOM par modern-web-types ou déclarez le membre manquant dans un fichier .d.ts global.
Comment déclarer une méthode manquante sur Element ou HTMLElement plutôt que sur Window ?
Fusionnez-la dans l'interface sur laquelle la spécification définit le membre. Dans un fichier .d.ts global sans import ni export de premier niveau, déclarez interface Element avec la nouvelle méthode. HTMLElement, SVGElement et les autres interfaces d'éléments étendent Element : elles en héritent toutes. La méthode startViewTransition() limitée à un élément est définie sur Element. La déclarer uniquement sur HTMLElement laisserait donc les éléments SVG non typés. Reprenez la signature exacte depuis MDN ou la spécification.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k