Skip to content

Build1 publisher3 min readPublished

One npm alias replaces TypeScript's two-engine DOM library

TypeScript's DOM types only cover APIs shipped by two browser engines, so startViewTransition, Long Animation Frames and fetchLater() all fail to compile. modern-web-types regenerates the same libraries at a one-engine threshold.

The Engineer · Build desk

What happened

  • Three APIs shipped in Chrome fail to typecheck against TypeScript's built-in libraries: startViewTransition on HTMLElement, the scripts property on PerformanceEntry, and fetchLater on Window.
  • Installation aliases the package to @typescript/lib-dom, the specifier TypeScript resolves for its DOM library, and TypeScript 6 or newer additionally requires libReplacement set to true.
  • A weekly GitHub Actions job regenerates the types from the latest w3c/webref data, diffs them against the published version and opens a PR when the output changes.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • capability Code that feature-detects before calling a new API now compiles without a per-project set of hand-written declarations copied from the last project.
  • decision Adoption is a project-wide call, because no file keeps the two-engine warning once the library is substituted; portability checks have to live in review or lint instead.
  • exposure A team taking the alias accepts an outside weekly release into the type layer of its build, where the only gate is a single PR approval.
  • precedent So long as libReplacement stays supported, the two-engine argument becomes a compiler setting each project decides for itself instead of a debate to win in TypeScript's repo.

`Property 'startViewTransition' does not exist on type 'HTMLElement'` is a claim about how many engines have shipped the method, not about your browser [2]. TypeScript's web API types come from a generator whose policy is to emit only APIs supported by at least two browser engines [1]. Element-scoped view transitions, the Long Animation Frame API's `scripts` property and `fetchLater()` are all in Chrome, and all three are safe to use now as progressive enhancements, according to the post [5][3][4].

The replacement works by name. TypeScript resolves its DOM library by looking for `@typescript/lib-dom`, so the install line aliases the package to that specifier: `npm install --save-dev @typescript/lib-dom@npm:modern-web-types` [7]. On TypeScript 6 and newer you also set `"libReplacement": true` in `compilerOptions` [8]. The swap happens at lib resolution, which means it applies to every file the program compiles, with no per-file opt-in [18].

modern-web-types is built on TypeScript's own generator with the support threshold set to one engine [6]. After the swap the compiler accepts `fetchLater()`, and it no longer distinguishes an API two engines ship from one only Chrome ships. Whether the call site is guarded becomes a question for code review. Contributors to TypeScript-DOM-lib-generator describe the two-engine rule as a balance between early adoption and web compatibility, the post says [11]. Its author disagrees: "But to be honest, I'm not convinced that this policy actually promotes web compatibility," he wrote on philipwalton.com [12], and he does not think the rule has been a net positive for the open web [13].

The generation is automated. It uses the same TypeScript-DOM-lib-generator and the same w3c/webref data sources TypeScript itself uses, with the threshold lowered from two engines to one [9]. A weekly GitHub Actions job reruns the generator against the latest data, compares the output against the published version and opens a PR when it differs; once that PR is approved a new version publishes, and approval is the only manual step, though the post says it may be automated later [10].

The post includes a table counting the extra declarations the one-engine threshold adds across DOM and WebWorker, and points readers at the repo's `report.md` for the current list [15]. The row its author singles out is the one covering interfaces already in constant use: properties and methods missing from `Document`, `Element`, `Navigator` and `Request` [14]. "Before starting this project, I knew there were a lot of APIs that were not in TypeScript's official libraries, but even I was surprised to discover just how big that number was," he wrote [16].

For a project that already feature-detects before calling anything new, the swap deletes the hand-copied declarations the author says he carried between projects for years and costs one compiler option [17][8].

What to watch

  • Whether PR approval gets automated, since it is currently the only manual step in the weekly publish pipeline.
  • Whether TypeScript's own generator lowers the two-engine threshold or ships an opt-in single-engine lib alongside the default.
  • Whether libReplacement remains a supported way to override the DOM lib in TypeScript 6 and later releases.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories