DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

TypeScript + Firefox DevTools: Source Maps, Debugging, and WebExtension Types

TypeScript brings static typing to JavaScript, but debugging TypeScript in Firefox DevTools requires some extra knowledge. Source maps are the key — they let Firefox show your original .ts files even though the browser runs compiled .js. This guide covers setup, debugging patterns, and TypeScript-specific tooling.

How Source Maps Work

When TypeScript compiles to JavaScript, it can generate a .map file:

myfile.ts  →  myfile.js + myfile.js.map
Enter fullscreen mode Exit fullscreen mode

The .map file tells Firefox DevTools: "line 42 in myfile.js corresponds to line 38 in myfile.ts."

Firefox then shows your original TypeScript in the debugger, not the compiled output.

tsconfig.json for Debugging

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "sourceMap": true,
    "inlineSources": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}
Enter fullscreen mode Exit fullscreen mode

Key flags:

  • "sourceMap": true — generates .map files alongside .js
  • "inlineSources": true — embeds source content in the map (works even if original files aren't served)
  • "inlineSourceMap": true — embeds the map inside the JS file (one file, easier for dev)

Setting Up Firefox DevTools for TypeScript

  1. Open DevTools → Settings (F1)
  2. Ensure "Enable Source Maps" is checked (it is by default)
  3. Open the Debugger tab
  4. Your .ts files appear in the file tree under the source map group
// src/counter.ts
interface Counter {
  count: number;
  increment(): void;
  reset(): void;
}

function createCounter(initial = 0): Counter {
  let count = initial;
  return {
    get count() { return count; },
    increment() { count++; },
    reset() { count = initial; }
  };
}

const counter = createCounter(10);
counter.increment(); // Set breakpoint here in DevTools
console.log(counter.count); // 11
Enter fullscreen mode Exit fullscreen mode

Set a breakpoint on counter.increment() — Firefox pauses and shows you counter.ts, not the compiled output.

TypeScript Type Errors in the Console

TypeScript errors are compile-time, not runtime. But type errors can manifest as runtime issues:

// TypeScript (compile error, but compiles with --noEmitOnError false):
function greet(name: string): string {
  return "Hello, " + name;
}

// If called with wrong type at runtime:
greet(42 as any); // No TS error but you passed a number
Enter fullscreen mode Exit fullscreen mode

Firefox DevTools console shows the runtime result ("Hello, 42"), not a type error. TypeScript's job is done at compile time.

For runtime type checking, use:

function greet(name: unknown): string {
  if (typeof name !== 'string') {
    throw new TypeError(`Expected string, got ${typeof name}`);
  }
  return "Hello, " + name;
}
Enter fullscreen mode Exit fullscreen mode

Now Firefox shows TypeError: Expected string, got number with the full stack trace.

Inspecting TypeScript Types in the Console

The console doesn't know TypeScript types at runtime — types are erased during compilation. But you can use typeof and structured inspection:

// In TypeScript source
interface User {
  id: number;
  name: string;
  roles: string[];
}

const user: User = { id: 1, name: 'Alice', roles: ['admin', 'user'] };
window.__debugUser = user; // Expose for console inspection
Enter fullscreen mode Exit fullscreen mode

In Firefox console:

// Types are gone, but structure remains
typeof __debugUser // "object"
Object.keys(__debugUser) // ["id", "name", "roles"]
__debugUser instanceof Object // true
Enter fullscreen mode Exit fullscreen mode

TypeScript with Firefox WebExtensions

Firefox extensions benefit from TypeScript — catch errors before publishing to AMO:

npm install --save-dev typescript @types/firefox-webext-browser
Enter fullscreen mode Exit fullscreen mode

tsconfig.json for Firefox extension:

{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["ES2017"],
    "strict": true,
    "sourceMap": true,
    "types": ["firefox-webext-browser"]
  }
}
Enter fullscreen mode Exit fullscreen mode
// background.ts
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // Types are inferred! tabId: number, changeInfo: browser.tabs.TabChangeInfo, etc.
  if (changeInfo.status === 'complete' && tab.url) {
    console.log('Tab loaded:', tab.url);
  }
});

// TypeScript catches this at compile time:
browser.tabs.query({ actve: true }); // Error: 'actve' is not assignable
Enter fullscreen mode Exit fullscreen mode

Debugging TypeScript with Vite

Vite is the most common bundler for TypeScript web apps, and it works well with Firefox:

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    sourcemap: true, // Enable source maps in production build
  },
});
Enter fullscreen mode Exit fullscreen mode

In development, Vite serves source maps automatically. Firefox DevTools shows your .ts files directly.

TypeScript Declaration Files

When using third-party libraries without TypeScript support, you can add types:

// types/mylib.d.ts
declare module 'mylib' {
  export function doThing(value: string): number;
  export const VERSION: string;
}
Enter fullscreen mode Exit fullscreen mode

These don't affect the compiled output, but TypeScript (and your IDE) will understand the types.

Practical TypeScript Patterns for Firefox Apps

// Custom error types
class NetworkError extends Error {
  constructor(
    message: string,
    public readonly status: number,
    public readonly url: string
  ) {
    super(message);
    this.name = 'NetworkError';
  }
}

// Type-safe event emitter
type EventMap = {
  'data': { id: number; value: string };
  'error': Error;
  'close': void;
};

class TypedEmitter<T extends Record<string, unknown>> {
  private listeners = new Map<keyof T, Set<(data: T[keyof T]) => void>>();

  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    if (!this.listeners.has(event)) this.listeners.set(event, new Set());
    this.listeners.get(event)!.add(listener as any);
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    this.listeners.get(event)?.forEach(fn => fn(data));
  }
}

const emitter = new TypedEmitter<EventMap>();
emitter.on('data', ({ id, value }) => console.log(id, value)); // fully typed
Enter fullscreen mode Exit fullscreen mode

TypeScript + Firefox DevTools: Performance

TypeScript compilation adds a build step. For fastest development iteration:

  1. Use tsc --watch or your bundler's watch mode
  2. Enable Firefox DevTools "Enable browser toolbox" for extension debugging
  3. Use about:debugging#/runtime/this-firefox to load and reload extensions without restarting Firefox
  4. Check the Browser Console (Ctrl+Shift+J) for extension background script errors

TypeScript's compile step catches a whole class of bugs before they reach the browser — pair it with Firefox DevTools for a solid development workflow.

Top comments (0)