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.