<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryuya</title>
    <description>The latest articles on DEV Community by Ryuya (@ryuyah).</description>
    <link>https://dev.to/ryuyah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4176598%2F721a0da1-a36b-477b-b2a5-e3340c6b70c1.png</url>
      <title>DEV Community: Ryuya</title>
      <link>https://dev.to/ryuyah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryuyah"/>
    <language>en</language>
    <item>
      <title>Type-checks pass, browsers crash: adding a Baseline check to a TypeScript project</title>
      <dc:creator>Ryuya</dc:creator>
      <pubDate>Tue, 13 Oct 2026 14:45:08 +0000</pubDate>
      <link>https://dev.to/ryuyah/type-checks-pass-browsers-crash-adding-a-baseline-check-to-a-typescript-project-4gl5</link>
      <guid>https://dev.to/ryuyah/type-checks-pass-browsers-crash-adding-a-baseline-check-to-a-typescript-project-4gl5</guid>
      <description>&lt;p&gt;&lt;code&gt;tsc&lt;/code&gt; exits cleanly, the pull request merges, and a few days later your error tracker starts collecting &lt;code&gt;is not a function&lt;/code&gt;. In February 2024, a developer using next-intl described what that looks like in &lt;a href="https://github.com/amannn/next-intl/issues/884" rel="noopener noreferrer"&gt;issue #884&lt;/a&gt;: people on slightly older browsers were triggering &lt;code&gt;t.replace(...).replaceAll is not a function&lt;/code&gt; in Sentry, and "My users are hitting error boundary and cannot use the web at the moment." &lt;code&gt;String.prototype.replaceAll&lt;/code&gt; is declared in TypeScript's ES2021 lib, so a type check has no reason to object. The fix, &lt;a href="https://github.com/amannn/next-intl/pull/885" rel="noopener noreferrer"&gt;PR #885&lt;/a&gt;, switched those calls to &lt;code&gt;replace&lt;/code&gt;. In May 2024, a &lt;a href="https://github.com/mermaid-js/mermaid/issues/5523" rel="noopener noreferrer"&gt;Mermaid issue&lt;/a&gt; showed the same pattern with &lt;code&gt;structuredClone&lt;/code&gt; and &lt;code&gt;Object.hasOwn&lt;/code&gt;: on an iPhone running Safari 14, Mermaid 10.9.0 failed with "Can't find variable: structuredClone", while 10.4.0 loaded.&lt;/p&gt;

&lt;p&gt;Types are also getting wider on purpose, and Philip Walton's &lt;a href="https://philipwalton.com/articles/modern-web-types/" rel="noopener noreferrer"&gt;modern-web-types&lt;/a&gt; now declares web APIs that have shipped in at least one stable browser engine. His post keeps browser support as a separate check: "I believe the best way to do that is to have tests that run in all the browsers you support. And to catch potential issues earlier in the feedback loop, you can also configure linter plugins like &lt;code&gt;eslint-plugin-baseline-js&lt;/code&gt; based on your siteâ€™s Baseline target."&lt;/p&gt;

&lt;p&gt;This post covers that earlier signal, written as a setup you can copy. By the end you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your normal &lt;code&gt;tsconfig.json&lt;/code&gt;, with the wide DOM types for editing,&lt;/li&gt;
&lt;li&gt;a second &lt;code&gt;tsconfig.baseline.json&lt;/code&gt; that type-checks JavaScript built-ins against a Baseline target, using &lt;a href="https://github.com/3ru/TypeScript-Baseline-lib-generator" rel="noopener noreferrer"&gt;typescript-baseline-lib&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;an ESLint config that checks DOM and built-in usage against the same target, using &lt;a href="https://github.com/3ru/eslint-plugin-baseline-js" rel="noopener noreferrer"&gt;eslint-plugin-baseline-js&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;two npm scripts for CI, and a pattern for code that is feature-detected or polyfilled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below was run on October 11, 2026. All output is copied from the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;If you have never hit a missing DOM type, you probably don't need &lt;code&gt;modern-web-types&lt;/code&gt;, and you may not need this either. If your team already enforces a Browserslist target with &lt;code&gt;eslint-plugin-compat&lt;/code&gt; and is happy with it, keep that. This setup is for projects that turned on wide DOM types and now want a CI step that says "this call is outside our target" before it reaches review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;typescript&lt;/td&gt;
&lt;td&gt;6.0.3 (tsc results repeated on 7.0.2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;modern-web-types (installed as &lt;code&gt;@typescript/lib-dom&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;0.6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;typescript-baseline-lib&lt;/td&gt;
&lt;td&gt;0.1.1 (Baseline snapshot 2026-10-02)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eslint&lt;/td&gt;
&lt;td&gt;10.12.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eslint-plugin-baseline-js&lt;/td&gt;
&lt;td&gt;0.7.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;typescript-eslint&lt;/td&gt;
&lt;td&gt;8.71.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I stayed on TypeScript 6.0.3 for the whole project because &lt;code&gt;typescript-eslint&lt;/code&gt; 8.71.1 lists TypeScript &lt;code&gt;&amp;lt;6.1.0&lt;/code&gt; as its peer range, and the lint step needs type information. I ran the same &lt;code&gt;tsc&lt;/code&gt; commands on TypeScript 7.0.2 and got the same diagnostics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; &lt;span class="nt"&gt;--save-exact&lt;/span&gt; typescript@6.0.3 typescript-baseline-lib@0.1.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"@typescript/lib-dom@npm:modern-web-types@0.6.0"&lt;/span&gt; eslint@10.12.0 &lt;span class="se"&gt;\&lt;/span&gt;
  eslint-plugin-baseline-js@0.7.5 typescript-eslint@8.71.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The sample code
&lt;/h2&gt;

&lt;p&gt;Two files. The first uses JavaScript built-ins that are Baseline Newly available but not yet Widely available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/builtins.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// set-methods: Newly available 2024-06-11&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// promise-try: Newly available 2025-01-07&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second uses element-scoped view transitions, the API behind the first type error Walton shows in his post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/dom.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;animateCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: wide DOM types for development
&lt;/h2&gt;

&lt;p&gt;This is the setup from the &lt;code&gt;modern-web-types&lt;/code&gt; README. Keep &lt;code&gt;lib&lt;/code&gt; as you had it and turn on &lt;code&gt;libReplacement&lt;/code&gt;, so TypeScript picks up &lt;code&gt;@typescript/lib-dom&lt;/code&gt; from &lt;code&gt;node_modules&lt;/code&gt; instead of its bundled DOM file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DOM"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"libReplacement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bundler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For comparison, here is a &lt;code&gt;tsconfig.control.json&lt;/code&gt; that extends this file and only sets &lt;code&gt;"libReplacement": false&lt;/code&gt;, so TypeScript uses its stock DOM lib:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx tsc -p tsconfig.control.json
src/dom.ts(2,13): error TS2339: Property 'startViewTransition' does not exist on type 'HTMLElement'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;libReplacement: true&lt;/code&gt;, &lt;code&gt;npx tsc -p tsconfig.json&lt;/code&gt; exits with no output. &lt;code&gt;Set#difference&lt;/code&gt; and &lt;code&gt;Promise.try&lt;/code&gt; also pass here, because &lt;code&gt;ESNext&lt;/code&gt; declares them. That is correct behavior. &lt;code&gt;lib&lt;/code&gt; describes a language version, so this config tells you the code is valid TypeScript, and nothing about which browsers can run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: a Baseline tsconfig for JavaScript built-ins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/3ru/TypeScript-Baseline-lib-generator" rel="noopener noreferrer"&gt;&lt;code&gt;typescript-baseline-lib&lt;/code&gt;&lt;/a&gt; is a replacement for TypeScript's standard JavaScript libs. Its declarations are generated from &lt;code&gt;web-features&lt;/code&gt; data, so a built-in that is outside the target has no declaration. It is a full replacement, which means it runs with &lt;code&gt;noLib&lt;/code&gt;. I put it in a second config so the editor keeps using the main one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.baseline.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noLib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"libReplacement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"typescript-baseline-lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@typescript/lib-dom"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details matter here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"lib": null&lt;/code&gt; clears the inherited &lt;code&gt;lib&lt;/code&gt; array, and &lt;code&gt;noLib&lt;/code&gt; stops TypeScript from loading its own &lt;code&gt;lib.*.d.ts&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@typescript/lib-dom&lt;/code&gt; is listed in &lt;code&gt;types&lt;/code&gt;. With &lt;code&gt;noLib&lt;/code&gt; on, no DOM lib loads by default, so you add &lt;code&gt;modern-web-types&lt;/code&gt; by name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx tsc -p tsconfig.baseline.json
src/builtins.ts(3,29): error TS2550: Property 'difference' does not exist on type 'Set&amp;lt;string&amp;gt;'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2025' or later.
src/builtins.ts(5,31): error TS2550: Property 'try' does not exist on type 'PromiseConstructor'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2025' or later.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't follow the suggestion at the end of those messages. It comes from TypeScript's generic TS2550 text, and changing &lt;code&gt;lib&lt;/code&gt; would turn the check off.&lt;/p&gt;

&lt;p&gt;To confirm what the program contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx tsc -p tsconfig.baseline.json --listFilesOnly
/path/to/project/src/builtins.ts
/path/to/project/src/dom.ts
/path/to/project/node_modules/typescript-baseline-lib/baseline.d.ts
/path/to/project/node_modules/typescript-baseline-lib/index.d.ts
/path/to/project/node_modules/@typescript/lib-dom/lib.dom.d.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your sources, the two Baseline files, and the DOM file. No &lt;code&gt;lib.es*.d.ts&lt;/code&gt;. With &lt;code&gt;skipLibCheck: false&lt;/code&gt;, the full &lt;code&gt;modern-web-types&lt;/code&gt; DOM declarations type-checked against the Baseline built-ins without a single declaration error.&lt;/p&gt;

&lt;p&gt;This config is responsible for JavaScript built-ins. DOM APIs come from &lt;code&gt;modern-web-types&lt;/code&gt; in both configs, so &lt;code&gt;el.startViewTransition()&lt;/code&gt; type-checks here too. DOM policy is the next step's job. If you would rather enforce DOM support in the type system itself, uhyo's &lt;a href="https://github.com/uhyo/TypeScript-Baseline-Types" rel="noopener noreferrer"&gt;&lt;code&gt;@baseline-types/dom-*&lt;/code&gt;&lt;/a&gt; packages provide a DOM lib cut to a Baseline target. That is the opposite choice from &lt;code&gt;modern-web-types&lt;/code&gt;, so pick one per config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: type-aware lint for the support policy
&lt;/h2&gt;

&lt;p&gt;Types answer "is this declared?". The linter answers "is this call inside our target?" for each expression in your code. I use &lt;a href="https://github.com/3ru/eslint-plugin-baseline-js" rel="noopener noreferrer"&gt;eslint-plugin-baseline-js&lt;/a&gt; for this step. Flat config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// eslint.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tseslint&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typescript-eslint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;baselineJs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eslint-plugin-baseline-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;languageOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tseslint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;parserOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./tsconfig.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tsconfigRootDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirname&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baseline-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baselineJs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;baselineJs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recommended-ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]({&lt;/span&gt; &lt;span class="na"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widely&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two choices in this file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;recommended-ts&lt;/code&gt; preset uses type information, which is how a call like &lt;code&gt;current.difference(removed)&lt;/code&gt; gets resolved to &lt;code&gt;Set&lt;/code&gt; rather than to an unknown object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parserOptions.project&lt;/code&gt; points at the main &lt;code&gt;tsconfig.json&lt;/code&gt;, the one with &lt;code&gt;modern-web-types&lt;/code&gt;. The rule resolves members through the types you load, so the wide DOM lib is what lets it identify &lt;code&gt;el.startViewTransition()&lt;/code&gt; as the &lt;code&gt;view-transitions-element-scoped&lt;/code&gt; feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx eslint src

/path/to/project/src/builtins.ts
  3:29  error  'difference' on Set is not Baseline Widely available (set-methods)  baseline-js/use-baseline
  5:31  error  'try' on Promise is not Baseline Widely available (promise-try)     baseline-js/use-baseline

/path/to/project/src/dom.ts
  2:13  error  'startViewTransition' on Element is not Baseline Widely available (view-transitions-element-scoped)  baseline-js/use-baseline

âœ– 3 problems (3 errors, 0 warnings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expected wide types and a strict policy to pull against each other. In practice they help each other: the types give the linter something concrete to identify, and the linter gives the wide types a policy to sit under.&lt;/p&gt;

&lt;p&gt;Other tools can fill this step. &lt;a href="https://github.com/amilajack/eslint-plugin-compat" rel="noopener noreferrer"&gt;&lt;code&gt;eslint-plugin-compat&lt;/code&gt;&lt;/a&gt; checks against a Browserslist config and supports a &lt;code&gt;polyfills&lt;/code&gt; setting. &lt;a href="https://github.com/koddsson/eslint-plugin-tscompat" rel="noopener noreferrer"&gt;&lt;code&gt;@koddsson/eslint-plugin-tscompat&lt;/code&gt;&lt;/a&gt; is type-aware and also reads Browserslist. If Babel, PostCSS, or your bundler already read Browserslist, its Baseline queries (&lt;code&gt;baseline widely available&lt;/code&gt;, &lt;code&gt;baseline 2024&lt;/code&gt;) give every tool the same policy name. Each tool ships its own copy of the data, so check that the versions line up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: wire it into CI
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"check:baseline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc -p tsconfig.baseline.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run both on pull requests. The &lt;a href="https://github.com/3ru/baseline-checks-demo" rel="noopener noreferrer"&gt;baseline-checks-demo&lt;/a&gt; repository has an &lt;a href="https://github.com/3ru/baseline-checks-demo/pull/1" rel="noopener noreferrer"&gt;example PR&lt;/a&gt; that replaces a manual Set filter with &lt;code&gt;Set.prototype.difference()&lt;/code&gt;. Both checks fail on that one-line change, with the same TS2550 and &lt;code&gt;set-methods&lt;/code&gt; messages as above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: code that is guarded or polyfilled
&lt;/h2&gt;

&lt;p&gt;Sooner or later you will want to use something outside the target on purpose. There are two common cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature detection
&lt;/h3&gt;

&lt;p&gt;Both checks report the call itself, with or without an &lt;code&gt;if&lt;/code&gt; around it. I think that is the right default: an exception to the support policy should be visible in the diff, with a reason a reviewer can read.&lt;/p&gt;

&lt;p&gt;This is the pattern I use. It passes both the Baseline tsconfig and ESLint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MaybeSetMethods&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReadonlySet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MaybeSetMethods&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// eslint-disable-next-line baseline-js/use-baseline -- feature-detected, fallback below&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The intersection type declares &lt;code&gt;difference&lt;/code&gt; as optional, so TypeScript forces the &lt;code&gt;typeof&lt;/code&gt; check before the call.&lt;/li&gt;
&lt;li&gt;The disable comment carries a reason, so a reviewer sees why the exception is safe.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;set-methods&lt;/code&gt; becomes Widely available (30 months after June 11, 2024, which is December 2026), you can delete the type, the comment, and the fallback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Polyfills
&lt;/h3&gt;

&lt;p&gt;If you load a polyfill, tell both checks about it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core-js/proposals/promise-try&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.baseline.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"typescript-baseline-lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"typescript-baseline-lib/allow/promise-try"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"@typescript/lib-dom"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// eslint.config.mjs, scoped to the files that import the polyfill&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/polyfilled/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baseline-js/use-baseline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widely&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ignoreFeatures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;promise-try&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;includeWebApis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type-aware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;includeJsBuiltins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type-aware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those two changes &lt;code&gt;Promise.try&lt;/code&gt; passed both checks and &lt;code&gt;Set#difference&lt;/code&gt; was still reported. The &lt;code&gt;allow/promise-try&lt;/code&gt; entry only adds declarations. It does not load any code, so the import line is what actually puts &lt;code&gt;Promise.try&lt;/code&gt; in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is responsible for what
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Handled by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which DOM APIs exist (including single-engine ones)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;modern-web-types&lt;/code&gt; in &lt;code&gt;tsconfig.json&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which JavaScript built-ins are inside the Baseline target&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;typescript-baseline-lib&lt;/code&gt; in &lt;code&gt;tsconfig.baseline.json&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether each DOM and built-in call is inside the target&lt;/td&gt;
&lt;td&gt;type-aware ESLint (&lt;code&gt;recommended-ts&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intentional exceptions&lt;/td&gt;
&lt;td&gt;optional-member types, &lt;code&gt;eslint-disable&lt;/code&gt; with a reason, &lt;code&gt;allow/*&lt;/code&gt;, &lt;code&gt;ignoreFeatures&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether the guard and fallback actually work&lt;/td&gt;
&lt;td&gt;tests in the browsers you support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last row is the one I would keep if I had to drop everything else. Walton puts cross-browser tests first, and I agree. Static checks give you an early signal. Running the code in a browser that lacks the feature is what tells you the fallback works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking the target
&lt;/h2&gt;

&lt;p&gt;I used Baseline Widely available because it is a reasonable default. web.dev's &lt;a href="https://web.dev/articles/how-to-choose-your-baseline-target" rel="noopener noreferrer"&gt;How to choose your Baseline target&lt;/a&gt; explains how to check a target against your own analytics. Baseline is defined by a set of core browsers, so look at your traffic for anything outside that set. It also does not track assistive technology support, which Adrian Roselli covers in &lt;a href="https://adrianroselli.com/2023/12/baseline-does-not-really-cover-baseline-support.html" rel="noopener noreferrer"&gt;Baseline Does Not Really Cover Baseline Support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving targets move. In the 2026-10-02 snapshot, &lt;code&gt;Object.groupBy&lt;/code&gt; and &lt;code&gt;Promise.withResolvers&lt;/code&gt; became Widely available in September 2026, while Set methods have not yet. Pin your package versions so a data update shows up as a dependency bump you can review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The setup comes down to one extra tsconfig, one ESLint config, and two scripts. &lt;code&gt;modern-web-types&lt;/code&gt; keeps doing its job of describing what browsers ship. The Baseline tsconfig and the linter hold the support policy. Feature detection and real browser tests handle whatever falls outside it.&lt;/p&gt;

&lt;p&gt;If you try this on a real codebase, I'd like to hear what the reports look like.&lt;/p&gt;

&lt;p&gt;Disclosure: I maintain eslint-plugin-baseline-js and typescript-baseline-lib.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>eslint</category>
    </item>
  </channel>
</rss>
