Skip to main content
Extension.js compiles TypeScript with SWC, which strips types and never checks them. Type checking is a separate step that you run with tsc. This page covers how that step resolves chrome.*, browser.*, and process.env.

What Extension.js generates

When a project uses TypeScript, extension dev and extension build write an extension-env.d.ts file beside package.json. It is regenerated on every run, so do not edit it. The file pulls in the ambient types that the extension package publishes:
extension-env.d.ts
Those references give you: The EXTENSION_* environment keys are typed here too. That is why process.env.EXTENSION_MODE resolves without extra setup.

Types that install with Extension.js

From Extension.js 4.1.19, the extension package lists the three type packages that its references need as dependencies: They install with extension, so tsc resolves chrome.* and browser.* in a new project without an extra install:
The build does not need these packages. SWC never reads the types, so the build succeeds with or without them.

Pin your own version

The extension package accepts any version of the three type packages. When your project declares one of them, npm, pnpm, and Bun reuse the copy that your project installs. When your tsconfig.json has no types array, TypeScript also loads the copy that your project declares. The version in your own package.json wins:
package.json

Projects on Extension.js 4.1.18 or older

Before 4.1.19, extension/types referenced chrome but did not install @types/chrome. A project that calls chrome.* without its own copy fails tsc:
Upgrade extension to 4.1.19 or later. If you stay on an older version, install the package by hand:
On those versions, browser has the type any until @types/webextension-polyfill is installed. To type browser.*, install that package too:

Type errors on browser.* after an upgrade

From 4.1.19, browser has the full webextension-polyfill type instead of any. Code that passed tsc before can show new type errors, for example a call to an API that the polyfill does not declare. Fix the call, or use chrome.* for an API that only Chromium ships. Read Cross-browser compatibility for the runtime side of the same choice.

Keep extension-env.d.ts in the include list

The generated file only helps when TypeScript reads it. The scaffolded tsconfig.json names it:
tsconfig.json
When Extension.js writes a tsconfig.json for a project that has none, that file carries no include array. TypeScript then reads every file under the project folder, so it finds extension-env.d.ts anyway. An include array of your own that omits the file breaks asset imports and the browser global.

Symptoms and fixes

Best practices

  • Treat extension-env.d.ts as build output. Commit it if you like, but never edit it.
  • Declare a type package in your own devDependencies only when you need a specific version of it.
  • Run tsc --noEmit in continuous integration. The Extension.js build does not fail on type errors.

Next steps