How to fix Global declaration in non-declaration file in TypeScript
This TypeScript error occurs when you try to declare global types, interfaces, or modules in a regular .ts file instead of a .d.ts declaration file. Global declarations must be placed in declaration files to avoid polluting the global namespace in implementation files.
Quick fix
Fixes TS2669
// WRONG - In a regular .ts file (src/utils.ts):
declare global {
interface Window {
myCustomProperty: string;
}
}
// Error: Global declaration in non-declaration file
// CORRECT - Create a declaration file (src/global.d.ts):
declare global {
interface Window {
myCustomProperty: string;
}
}
// Also correct - In a declaration file for module augmentations (src/module-augmentations.d.ts):
declare module "some-module" {
export interface SomeInterface {
extraField: boolean;
}
}- Move global declarations to a .d.ts declaration file
- Ensure declaration files are included in tsconfig.json
- Use type-only imports in declaration files
- Fix common global augmentation patterns
// WRONG - In a regular .ts file (src/utils.ts):declare global {interface Window {myCustomProperty: string;}}// Error: Global declaration in non-declaration file// CORRECT - Create a declaration file (src/global.d.ts):declare global {interface Window {myCustomProperty: string;}}// Also correct - In a declaration file for module augmentations (src/module-augmentations.d.ts):declare module "some-module" {export interface SomeInterface {extraField: boolean;}}Global declaration in non-declaration file
What this error means
The "Global declaration in non-declaration file" error (TS2669) appears when you attempt to declare global types, interfaces, modules, or namespaces in a regular TypeScript implementation file (.ts or .tsx) rather than a declaration file (.d.ts). TypeScript enforces a clear separation between: 1. **Declaration files (.d.ts)**: Contain type information only, no runtime code 2. **Implementation files (.ts/.tsx)**: Contain both type information and runtime code Global declarations (using `declare global`, `declare module`, or ambient declarations) should only appear in declaration files because they affect the global namespace. When placed in implementation files, they can cause unexpected side effects and namespace pollution. The error prevents you from accidentally creating global types that could conflict with other parts of your codebase or third-party libraries. TypeScript wants you to be explicit about where global declarations live to maintain code clarity and prevent hard-to-debug type conflicts.
Typical symptoms
- Compilation fails with 'Global declaration in non-declaration file' error
- Error appears when using `declare global`, `declare module`, or ambient declarations in .ts files
- IDE shows red underlines on global declaration statements
- Error code TS2669 appears in compiler output
- Code works when declarations are moved to .d.ts files
- Global augmentations fail to take effect when declared in implementation files
Common causes
- Using `declare global` block in a regular .ts/.tsx implementation file
- Declaring ambient modules with `declare module 'module-name'` in implementation files
- Adding global type augmentations in files that contain runtime code
- Mixing type declarations with implementation logic in the same file
- Attempting to extend global interfaces (like Window or NodeJS.Global) in .ts files
- Creating global namespace declarations outside of .d.ts files
- Adding `declare` statements for third-party module augmentations in implementation files
- Confusing declaration files with implementation files in project structure
How to fix "Global declaration in non-declaration file"
Create a separate declaration file (e.g., global.d.ts, types.d.ts, or declarations.d.ts) and move all global declarations there:
// WRONG - In a regular .ts file (src/utils.ts):
declare global {
interface Window {
myCustomProperty: string;
}
}
// Error: Global declaration in non-declaration file
// CORRECT - Create a declaration file (src/global.d.ts):
declare global {
interface Window {
myCustomProperty: string;
}
}
// Also correct - In a declaration file for module augmentations (src/module-augmentations.d.ts):
declare module "some-module" {
export interface SomeInterface {
extraField: boolean;
}
}Declaration files should:
- Have the .d.ts extension
- Contain only type declarations (no import statements unless they're type-only)
- Be included in your tsconfig.json include or files array
Make sure your declaration files are included in TypeScript's compilation:
// tsconfig.json
{
"compilerOptions": {
// ... other options
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.d.ts", // Include declaration files
"types/**/*.d.ts" // If you have a types folder
],
"exclude": ["node_modules"]
}If you have a dedicated types folder:
{
"include": [
"src/**/*",
"types/**/*.d.ts"
]
}For project references or monorepos, ensure declaration files are in the referenced project's include list.
Declaration files should not contain runtime code. If you need to reference types from other modules, use type-only imports:
// WRONG - Regular import in .d.ts file:
import { SomeType } from "./some-module";
// Error: Import declarations in a namespace cannot reference a module
// CORRECT - Type-only import:
import type { SomeType } from "./some-module";
// Also correct - Using import() type syntax:
declare global {
type MyType = import("./some-module").SomeType;
}
// For third-party modules:
import type { ReactNode } from "react";
declare global {
type CustomComponentProps = {
children: ReactNode;
};
}Type-only imports (import type) ensure no runtime code is included in declaration files.
Here are correct patterns for common global augmentations:
// 1. Extending global interfaces (Window, Document, etc.)
// In src/global.d.ts:
declare global {
interface Window {
myApp: {
version: string;
init(): void;
};
}
interface Document {
customMethod(): void;
}
}
// 2. Augmenting Node.js global
// In src/node-global.d.ts:
declare global {
namespace NodeJS {
interface Global {
myGlobalVar: string;
}
interface ProcessEnv {
CUSTOM_ENV_VAR: string;
}
}
}
// 3. Module augmentations for third-party libraries
// In src/module-augmentations.d.ts:
declare module "express" {
interface Request {
user?: {
id: string;
name: string;
};
}
}
// 4. Adding types to built-in objects
// In src/builtin-augmentations.d.ts:
interface Array<T> {
customMethod(): T[];
}
interface String {
toTitleCase(): string;
}All these should be in .d.ts files, not .ts files.
If you have a file that contains only types, convert it to a .d.ts file:
// BEFORE: src/types.ts (implementation file with only types)
export interface User {
id: string;
name: string;
}
export type Status = "active" | "inactive" | "pending";
// Global declaration causing error:
declare module "*.svg" {
const content: string;
export default content;
}
// AFTER: src/types.d.ts (declaration file)
export interface User {
id: string;
name: string;
}
export type Status = "active" | "inactive" | "pending";
// Global declaration now allowed:
declare module "*.svg" {
const content: string;
export default content;
}To convert:
1. Change file extension from .ts to .d.ts
2. Remove any runtime code (functions, classes with implementations, etc.)
3. Keep only type declarations, interfaces, and type aliases
4. Update imports to use import type if needed
5. Update any import statements in other files to reference the .d.ts file
Organize your declaration files logically:
# Recommended structure
src/
├── index.ts # Main entry point
├── global.d.ts # Global augmentations
├── types/
│ ├── index.d.ts # Re-export all types
│ ├── user.d.ts # User-related types
│ ├── api.d.ts # API types
│ └── module-augmentations.d.ts # Third-party module types
├── declarations/
│ ├── images.d.ts # Image module declarations
│ ├── css-modules.d.ts # CSS module declarations
│ └── env.d.ts # Environment variable types
└── lib/
└── types.d.ts # Library-specific types
# Alternative flat structure
src/
├── types.d.ts # All type declarations
├── global-augmentations.d.ts
└── module-declarations.d.tsUpdate tsconfig.json to include all declaration locations:
{
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.d.ts",
"src/types/**/*.d.ts",
"src/declarations/**/*.d.ts"
]
}This keeps global declarations separate from implementation logic.
Advanced notes
### Understanding Declaration Files vs Implementation Files
Declaration Files (.d.ts):
- Contain only type information
- No runtime code allowed
- Can use declare keyword for ambient declarations
- Support global augmentations (declare global)
- Can augment modules (declare module)
- Compiled to empty .js files (or no output)
Implementation Files (.ts/.tsx):
- Contain both types and runtime code
- Cannot have global declarations
- Produce .js output when compiled
- Should import types from declaration files
### Ambient Declarations
Ambient declarations tell TypeScript about types that exist elsewhere (in JavaScript files, runtime environments, or third-party libraries):
// In a .d.ts file
declare const VERSION: string; // Global constant
declare function log(message: string): void; // Global function
declare class MyClass { // Global class
constructor(value: string);
method(): void;
}
declare namespace MyNamespace { // Global namespace
export interface Config {
enabled: boolean;
}
}These should always be in .d.ts files.
### Module Augmentation vs Declaration Merging
Module Augmentation (for third-party modules):
// In a .d.ts file
declare module "express" {
interface Request {
user?: User;
}
}Declaration Merging (for your own code):
// In user.ts (implementation file)
export interface User {
id: string;
name: string;
}
// In user-roles.ts (another implementation file)
export interface User {
roles: string[]; // Merges with the User interface above
}Declaration merging works in .ts files because you're merging within the same module scope, not globally.
### Global vs Module Scope
Global scope (affects entire project):
// In a .d.ts file
declare global {
interface Window {
myGlobal: string;
}
}
// Can be used anywhere without importModule scope (needs import/export):
// In a .ts file
export interface MyInterface {
value: string;
}
// Must be imported to use### Checking Declaration File Output
Verify your declaration files produce correct output:
# Generate declaration files
npx tsc --declaration --emitDeclarationOnly
# Check what's emitted
ls dist/*.d.ts
# Test if a file is valid declaration-only
npx tsc --noEmit --isolatedModules your-file.d.ts### Common Pitfalls
1. Mixing imports: Regular import statements in .d.ts files (use import type)
2. Export assignments: export = something in .d.ts files (use export default or named exports)
3. Runtime code: Any function/class implementations in .d.ts files
4. Missing includes: Declaration files not included in tsconfig.json
5. Circular references: Type-only imports that create circular dependencies
### TypeScript Configuration for Declarations
Recommended tsconfig.json settings:
{
"compilerOptions": {
"declaration": true, // Generate .d.ts files
"declarationMap": true, // Generate source maps for declarations
"emitDeclarationOnly": false, // Set to true for declaration-only projects
"skipLibCheck": true, // Skip checking library declaration files
"types": [], // Explicitly include global types
"typeRoots": ["./node_modules/@types", "./types"] // Where to find declaration files
}
}For library projects that only export types:
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true, // Don't emit .js files
"outDir": "dist"
}
}Frequently asked questions
- What does "TS2669" mean?
- ts). TypeScript enforces a clear separation between: 1. ts): Contain type information only, no runtime code 2.
- How do I fix "TS2669"?
- This TypeScript error occurs when you try to declare global types, interfaces, or modules in a regular .ts file instead of a .d.ts declaration file. Global declarations must be placed in declaration files to avoid polluting the global namespace in implementation files.
- What causes "TS2669"?
- Using declare global block in a regular .ts/.tsx implementation file Declaring ambient modules with declare module 'module-name' in implementation files Adding global type augmentations in files that contain runtime code
- Is the "TS2669" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚TypeScript Official Documentation
- 📖TypeScript Handbook - Declaration Files Introduction(Official Docs)
- 📖TypeScript Handbook - Modules(Official Docs)
- 📖TypeScript Handbook - Declaration Files by Example(Official Docs)
- 📖TypeScript Handbook - Declaration Merging(Official Docs)
- 📖TypeScript Handbook - Modules Theory(Official Docs)