TypeScript development with Firefox is a strong combination. Firefox's DevTools have excellent TypeScript support through source maps, and there are Firefox-specific considerations when developing TypeScript-heavy applications. This guide covers the development workflow, common issues, and debugging patterns.
TypeScript in the Browser Context
TypeScript doesn't run natively in the browser — it compiles to JavaScript. The browser sees JavaScript, but with source maps configured correctly, Firefox DevTools shows you the original TypeScript source.
// tsconfig.json for web development
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"strict": true,
"sourceMap": true, // generates .js.map files
"outDir": "./dist",
"lib": ["DOM", "DOM.Iterable", "ES2022"]
}
}
The critical setting: "sourceMap": true. This generates .js.map files that Firefox uses to show TypeScript in the debugger.
Firefox Debugger with TypeScript
With source maps active, the Firefox Debugger shows TypeScript files in the Sources panel. Set breakpoints directly in .ts files — they pause at the right TypeScript location even though the browser runs compiled JavaScript.
Key features:
- Type information shown in hover tooltips (if the bundler preserves it)
- TypeScript file names in error stack traces
- Accurate variable inspection (variable names match TypeScript names, not mangled JavaScript)
If source maps aren't loading: check Network tab for failed .map file requests, or check that your bundler is outputting source maps to the correct path.
TypeScript DOM Types
TypeScript's lib.dom.d.ts declarations match the browser APIs. For Firefox-specific APIs:
// Firefox-specific APIs don't have TypeScript types by default
// Augment the global types:
declare global {
interface Window {
InstallTrigger?: {
install(urls: Record<string, string>): void;
};
}
}
// Check for Firefox
const isFirefox = typeof window.InstallTrigger !== 'undefined';
For WebExtensions TypeScript types:
npm install --save-dev @types/webextension-polyfill
Strict Mode and Browser Compatibility
TypeScript's strict mode catches bugs that become runtime errors in Firefox:
// Strict catches this:
function getElement(id: string) {
return document.getElementById(id); // Returns Element | null
}
// Without strict check, this would throw at runtime in Firefox:
const el = getElement('missing');
el.style.color = 'red'; // Runtime: Cannot set properties of null
// With strict, TypeScript forces the null check:
const el = getElement('missing');
if (el) {
el.style.color = 'red'; // Safe
}
TypeScript with Web APIs
Browser APIs have TypeScript types that reflect Firefox's implementation:
// getUserMedia is typed
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720 },
audio: true
});
// TypeScript knows stream is MediaStream
// Fetch API with typed responses
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json() as User; // type assertion
return data;
}
// IndexedDB with typed wrapper
interface UserStore {
id: number;
name: string;
email: string;
}
TypeScript Build Tools for Firefox Development
Vite (recommended for most projects):
npm create vite@latest my-app -- --template vanilla-ts
cd my-app && npm install && npm run dev
Vite generates source maps by default in dev mode. Firefox DevTools shows TypeScript files directly.
webpack:
// webpack.config.js
module.exports = {
devtool: 'source-map', // Full source maps for production debugging
// devtool: 'eval-source-map', // Faster rebuilds in development
module: {
rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }]
}
};
esbuild (fastest builds):
esbuild src/main.ts --bundle --sourcemap --outfile=dist/bundle.js
Firefox Extension Development with TypeScript
TypeScript is well-suited for Firefox extension development:
// manifest.json (still plain JSON)
// TypeScript for background script:
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// TypeScript knows the types of all parameters
if (changeInfo.status === 'complete' && tab.url) {
handleTabComplete(tabId, tab.url);
}
});
function handleTabComplete(tabId: number, url: string): void {
// typed parameters prevent common errors
}
The webext-browser package provides TypeScript types for the WebExtensions API:
npm install --save-dev @types/firefox-webext-browser
Type-Safe API Integration
Use TypeScript interfaces to type your API responses:
interface GitHubUser {
login: string;
id: number;
name: string | null;
public_repos: number;
}
async function getGitHubUser(username: string): Promise<GitHubUser> {
const response = await fetch(`https://api.github.com/users/${username}`);
const data: unknown = await response.json();
// Runtime validation (zod is popular for this):
// return UserSchema.parse(data);
// Or type assertion with comment:
return data as GitHubUser;
}
Debugging TypeScript Errors in Firefox
When TypeScript compiles to JavaScript, type errors appear in the terminal (tsc compiler), not in Firefox. But runtime errors in Firefox have useful stack traces:
- TypeError in Firefox console → click the file link → jumps to TypeScript source (with source maps)
- The error message often points to where TypeScript's optional chaining (
?.) would have prevented the issue
Patterns that prevent Firefox runtime errors:
// Instead of:
document.getElementById('app').style.color = 'red'; // Possible null
// TypeScript forces:
const app = document.getElementById('app');
if (app instanceof HTMLElement) {
app.style.color = 'red'; // Type-narrowed to HTMLElement
}
// Or:
(document.getElementById('app') as HTMLElement).style.color = 'red'; // Explicit assertion
Testing TypeScript in Firefox
For cross-browser TypeScript testing with Playwright:
import { test, expect, Browser } from '@playwright/test';
test('works in Firefox', async ({ browser }: { browser: Browser }) => {
const page = await browser.newPage();
await page.goto('http://localhost:5173');
await expect(page.locator('#result')).toHaveText('Success');
});
Configure playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } }
]
});
Top comments (0)