This error occurs when you attempt to augment a TypeScript module, but the target resolves to a namespace or value export rather than an ES module. Module augmentation requires the target to be a proper module with export declarations, not a non-module entity.
The 'Cannot augment module with a non-module declaration' error indicates a mismatch between how a library exports its code and how you're trying to extend it. Module augmentation in TypeScript uses the 'declare module' syntax to add new types, interfaces, or declarations to existing modules. However, this only works when the target is an actual ES module that uses export statements. If the library uses namespace declarations or value exports instead, TypeScript cannot merge your augmentation with the original declarations. This typically happens with older libraries that use the 'declare namespace' pattern or export a single value rather than named exports. The error prevents you from accidentally creating conflicting declarations that would override rather than extend the original module.
Check how the library exports its code by examining the type definitions. Open node_modules/@types/library-name/index.d.ts:
// ❌ Namespace-based (non-module)
declare namespace MyLibrary {
interface Config {
// ...
}
}
// ✓ Module-based (can be augmented)
declare module 'my-library' {
export interface Config {
// ...
}
}If the library uses 'declare namespace', you need to use namespace augmentation instead of module augmentation.
If the library is defined as a namespace, augment it using namespace syntax instead of module syntax:
// ❌ Wrong - trying to augment a namespace as a module
declare module 'jquery' {
interface JQuery {
customPlugin(): void;
}
}
// ✓ Correct - augment the namespace directly
declare namespace JQuery {
interface JQuery {
customPlugin(): void;
}
}For global namespaces, simply reopen the namespace declaration without the 'module' keyword.
TypeScript requires your augmentation file to be a module (have at least one import or export). Add an export statement if missing:
// types/augmentation.d.ts
// Add this line to make it a module
export {};
// Now your module augmentation will work
declare module 'some-library' {
export interface Config {
newProperty: string;
}
}Without the 'export {}', TypeScript treats the file as a global script, which can cause declaration conflicts.
If the library exports a single value or function, you may need to augment it globally rather than as a module:
// ❌ Won't work if library exports a value
declare module 'lodash' {
export interface LoDashStatic {
customMethod(): void;
}
}
// ✓ Augment the global interface instead
import lodash from 'lodash';
declare global {
interface LoDashStatic {
customMethod(): void;
}
}
export {};This approach works when the library's types are defined globally.
Library updates can change from namespace to module exports (or vice versa). Ensure your @types package version matches your library version:
# Check installed versions
npm list library-name @types/library-name
# Update @types to match library version
npm install -D @types/library-name@latest
# Or match specific version
npm install -D @types/[email protected]Review the library's changelog to see if the export pattern changed between versions.
If the library's types are incompatible with augmentation, create a custom declaration file:
// types/library-name.d.ts
declare module 'library-name' {
export interface Config {
// Copy original properties
existingProp: string;
// Add your new properties
customProp: number;
}
export function initialize(config: Config): void;
}This replaces the original type definitions but gives you full control over the structure.
Module vs. Namespace: TypeScript distinguishes between modules (files with imports/exports) and namespaces (global declarations). This distinction affects how you extend types. Module augmentation only works with ES modules, not namespaces or UMD patterns that TypeScript resolves as namespaces. Declaration Merging: When augmentation succeeds, TypeScript merges your declarations with the original using declaration merging. This only works when both declarations are of compatible types (both interfaces, both namespaces, etc.). Global vs. Module Scope: Files without imports/exports are treated as global scripts. Adding 'export {}' converts them to modules with local scope, which is required for 'declare module' to work correctly. Legacy Libraries: Many older JavaScript libraries (jQuery, Knockout, Lodash) use namespace patterns. When working with these libraries, prefer namespace augmentation or consider using their modern ES module versions if available. TypeScript Module Resolution: The 'moduleResolution' setting in tsconfig.json affects how TypeScript interprets module declarations. Using 'node' or 'node16' resolution can change whether a library is treated as a module or namespace.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript