Global Type Augmentation
Problem
TypeScript doesn’t know about custom Window properties, custom events, or non-standard module imports (like SCSS). This causes type errors even when the runtime behavior is correct.
Solution
Create types/*.d.ts files with interface augmentation to extend global types.
Code Example
// types/quartz.d.ts
// Type definitions for nominal types
type SlugLike<T extends string> = string & { __brand: T };
type FullSlug = SlugLike<'full'>;
// SCSS module declarations
declare module '*.scss' {
const content: Record<string, string>;
export default content;
}
// Custom Window properties and events
declare global {
interface CustomEventMap {
nav: CustomEvent<{ url: FullSlug }>;
prenav: CustomEvent<Record<string, never>>;
themechange: CustomEvent<{ theme: 'dark' | 'light' }>;
readermodechange: CustomEvent<{ mode: 'on' | 'off' }>;
}
interface Window {
addCleanup(fn: () => void): void;
spaNavigate(url: URL | FullSlug): void;
}
interface DocumentEventMap extends CustomEventMap {
nav: CustomEventMap['nav'];
prenav: CustomEventMap['prenav'];
themechange: CustomEventMap['themechange'];
readermodechange: CustomEventMap['readermodechange'];
}
}
// Content data types
declare global {
interface ContentDetails {
title?: string;
slug?: FullSlug;
content?: string;
tags?: string[];
[key: string]: unknown;
}
type ContentIndex = { [key: string]: ContentDetails };
function fetchData(): Promise<ContentIndex>;
}
export {};Usage
1. Create the Declaration File
Place in types/quartz.d.ts (or any .d.ts file in a types/ directory).
2. Configure TypeScript
Ensure tsconfig.json includes the types directory:
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"]
},
"include": ["types/**/*.d.ts", "**/*.ts"]
}3. Use Extended Types
// Now TypeScript knows about these
window.addCleanup(() => cleanup());
document.addEventListener('nav', (e) => {
const slug: FullSlug = e.detail.url;
});Trade-offs
When to Use
- Extending global objects - Window, Document, Navigator
- Custom events with typed payloads - Ensures
e.detailis properly typed - Non-standard module imports - SCSS, images, JSON files
- Third-party libraries without types - When
@types/*doesn’t exist
When NOT to Use
- Types that can be inferred - Let TypeScript do its job
- Local-scope-only types - Use regular
interfaceortypedeclarations - Types for your own modules - Export types from the source file
Key Points
declare global {}- Required to augment global scope from a moduleexport {}- Makes the file a module (required fordeclare global)- Interface merging - Interfaces with same name combine their members
declare module "*.scss"- Wildcard module declarations for import patterns
Source
Extracted from Knowledge-Vault during type safety audit (January 2026).
Related
- quartz-type-safety - Full context on Quartz type errors
- quartz-spa-navigation - SPA event system that uses these types