← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Module Augmentation & Declaration Merging

TypeScript 2.0 Advanced
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated detection is 'no' and the code_pattern targets symptom workarounds like `req as any` rather than the misconfigured augmentation itself. TypeScript won't error loudly when augmentation is set up incorrectly in a script file — it simply silently ignores the declarations, meaning the missing types only surface when developers notice `any` spreading or missing properties during code review or testing, not at compile time with a clear error.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix describes creating a .d.ts file with a specific declare module pattern and ensuring export {} is present, plus including it in tsconfig. This is slightly more than a one-line patch (it may touch a new file and tsconfig), but it's contained within one component/configuration area without cross-cutting changes.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). Once the augmentation .d.ts file is correctly set up, it stays localized to type infrastructure. It applies to web and cli contexts but the structural weight is limited — maintainers only need to be aware of where augmentation files live. It's not a strong architectural gravitational pull, just a small ongoing tax if the augmented interfaces evolve.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field shows developers incorrectly believe they must fork library types, and the common_mistakes reveal multiple non-obvious pitfalls: augmentation silently does nothing in script files (no import/export), the module path string must exactly match, and augmentation cannot remove or override existing declarations. These behaviors contradict intuitive expectations — a developer who writes `declare module` in a plain .ts file will see no error but get no augmentation, which contradicts how most TypeScript constructs behave.

About DEBT scoring →

Also Known As

declaration merging interface merging global augmentation ambient declarations module declaration

TL;DR

Module augmentation lets you extend third-party or global type definitions without modifying their source — adding properties to existing interfaces, modules, or the global scope.

Explanation

Declaration merging: two interface declarations with the same name are merged into one. Module augmentation: declare module 'express' { interface Request { user?: User } } adds user to Express's Request without forking. Global augmentation: declare global { interface Window { analytics: Analytics } } extends the DOM's Window. Must be in a module file (has import/export) — use export {} to make a plain .ts file a module. Use cases: Express/Fastify request decorators, Vue/Nuxt global properties ($store, $router), env variables on process.env, custom jest matchers. Ambient declarations (declare module '*.svg') type non-JS imports.

Diagram

flowchart TD
    LIB[express/index.d.ts<br/>interface Request - base]
    AUG[types/express.d.ts<br/>declare module express<br/>interface Request - user User]
    MERGED[Merged Request<br/>base fields + user: User]
    LIB --> MERGED
    AUG --> MERGED
    MERGED --> APP[All req objects fully typed]

Watch Out

Module augmentation is additive only — you cannot change the type of an existing property. If a library later adds the same property with a different type, you'll get a merge conflict error.

Common Misconception

You need to fork a library's type definitions to add properties to them — module augmentation lets you extend them in your own codebase without touching node_modules.

Why It Matters

Without augmentation, adding custom properties to Express Request or Vue's this context requires unsafe any casts throughout the codebase — augmentation keeps things typed at the source.

Common Mistakes

  • Writing augmentation in a script file (no import/export) instead of a module file — declare module has no effect in script files.
  • Augmenting the wrong module path — must exactly match the string used in import.
  • Trying to remove or override existing declarations — augmentation only adds, never replaces.
  • Forgetting to include the augmentation .d.ts in tsconfig include — the file must be picked up by the compiler.

Avoid When

  • When augmenting would hide a genuine type incompatibility — don't use augmentation to silence errors.
  • For types you own — just edit the source directly.

When To Use

  • Adding custom properties to Express/Fastify Request or Response in middleware.
  • Typing global variables injected by build tools (import.meta.env, __webpack_public_path__).
  • Extending third-party interfaces (Vue component options, Jest matchers) without forking.

Code Examples

💡 Note
Shows Express Request augmentation — instead of any casts everywhere, a single .d.ts file adds the user property to all Request objects with proper type safety.
✗ Vulnerable
// Unsafe — req.user typed as any everywhere
app.use((req: any, res, next) => {
    req.user = getUserFromToken(req.headers.authorization);
    next();
});

// Later:
const user = req.user; // any — no type safety
✓ Fixed
// types/express.d.ts — module augmentation
import { User } from './models';

declare module 'express-serve-static-core' {
    interface Request {
        user?: User;
    }
}

export {}; // makes this a module file

// Now everywhere in the app:
app.use((req, res, next) => {
    req.user = getUserFromToken(req.headers.authorization); // typed
    next();
});

router.get('/profile', (req, res) => {
    const user = req.user; // User | undefined — fully typed
});

Added 11 Apr 2026
Views 108
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
3 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S
No pings yet today
No pings yesterday
SEMrush 9 PetalBot 9 Ahrefs 7 Google 6 Bing 5 Perplexity 3 Scrapy 3 Twitter/X 3 Meta AI 2 Applebot 2 Sogou 2 ChatGPT 1 Unknown AI 1 Brave Search 1
crawler 53 crawler_json 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Create a .d.ts file with declare module 'package-name' { interface ExistingInterface { yourProp: YourType } } and ensure it has export {} to be treated as a module.
📦 Applies To
typescript 2.0 web cli Express Vue Nuxt Jest
🔗 Prerequisites
🔍 Detection Hints
req as any|\(req: any\)
Auto-detectable: ✗ No typescript
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: File


✓ schema.org compliant