This error occurs when you try to use the 'declare' keyword in a regular .ts file instead of a .d.ts declaration file. Ambient declarations are type-only declarations that can only exist in .d.ts files or within declare namespace blocks.
This error indicates that TypeScript encountered an ambient declaration in a context where implementations are required. Ambient declarations are type-only declarations made with the `declare` keyword. They exist purely for the type system at compile-time and don't generate any JavaScript code. According to TypeScript's type system rules, ambient declarations can only appear in: 1. **Declaration files (.d.ts files)** - Files dedicated to type definitions 2. **Declare namespace blocks** - Inside `declare namespace { }` or `declare module { }` blocks 3. **Global scope of .d.ts files** - Top-level declarations When you use `declare` in a regular .ts file (which is a "non-ambient context" because it contains actual implementations), TypeScript throws this error because it doesn't know how to reconcile type-only declarations with executable code.
First, locate the problematic declare statement in your code:
// ❌ WRONG - in a regular .ts file
declare namespace MyNamespace {
interface MyInterface {
name: string;
}
}
export function doSomething() {
// implementation
}The error occurs because you're mixing declare (type-only) with executable code (export function) in the same file.
The primary solution is to move ambient declarations to a dedicated declaration file with the .d.ts extension:
Create a new file: src/types/my-namespace.d.ts
// ✅ CORRECT - in a .d.ts file
declare namespace MyNamespace {
interface MyInterface {
name: string;
}
function doSomething(): void;
}Declaration files (.d.ts) are treated as ambient contexts where declare statements are allowed and expected. They contain only type information without implementations.
Keep your .ts files for implementation code and .d.ts files for types:
File structure:
src/
├── types/
│ └── my-namespace.d.ts # Type definitions only
├── lib/
│ └── my-module.ts # Implementation code
└── index.tsExample separation:
my-namespace.d.ts:
declare namespace MyNamespace {
interface User {
id: string;
name: string;
}
function getUserById(id: string): User | null;
}my-module.ts:
namespace MyNamespace {
export interface User {
id: string;
name: string;
}
export function getUserById(id: string): User | null {
// implementation
return null;
}
}
export = MyNamespace;This clearly separates type declarations from executable code.
If you must use declare in a .ts file, wrap it in a declare namespace or declare module block:
// ✅ This is allowed - declare is inside a namespace block
declare namespace GlobalTypes {
interface Window {
customProperty: string;
}
}
// ✅ This is allowed - augmenting module types
declare module 'express' {
interface Request {
userId?: string;
}
}
// Implementation code can follow
export function setupMiddleware() {
// ...
}The declare statement creates an ambient (type-only) context within a limited scope, allowing it to coexist with implementation code.
If you need to both define types AND provide implementation, remove the declare keyword:
// ❌ WRONG
declare interface User {
name: string;
}
// ✅ CORRECT - no declare keyword
interface User {
name: string;
}
class UserClass implements User {
name: string = '';
}
export { User, UserClass };Only use declare when describing external types that are defined elsewhere. For your own types and implementations in .ts files, omit the declare keyword entirely.
Ensure your tsconfig.json is properly configured to include declaration files:
{
"compilerOptions": {
"declaration": true,
"declarationDir": "./dist/types",
"outDir": "./dist",
"rootDir": "./src",
"typeRoots": ["./node_modules/@types", "./src/types"],
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Key settings:
- typeRoots - Tells TypeScript where to find .d.ts files
- declaration: true - Generates .d.ts files from your TypeScript sources
- Include both .ts and .d.ts files in your includes/excludes
### Understanding Ambient vs Non-Ambient Context
Ambient context refers to any code that exists purely in the type system without producing JavaScript runtime code. This includes:
- .d.ts files
- declare namespace blocks
- declare module blocks
- Global declare statements in .d.ts files
Non-ambient context refers to regular .ts files that produce JavaScript implementations.
### Common Patterns with Declare
Declaring global variables:
// ✅ In a .d.ts file or at top-level
declare global {
var globalVar: string;
}Extending external libraries:
// ✅ In a .d.ts file
declare module 'some-library' {
export interface SomeInterface {
newProperty: string;
}
}Ambient module federation:
// ✅ In a .d.ts file
declare module '*.json' {
const content: Record<string, unknown>;
export default content;
}### File Naming Conventions
- .d.ts - Declaration files (types only, no implementation)
- .ts - Implementation files (can include types but should not use declare at top level)
- .test.ts, .spec.ts - Test files (implementation)
### Common Migration Path
If you have a large .ts file with both types and implementations mixed with declare statements:
1. Create a corresponding .d.ts file with same base name
2. Move all declare statements there
3. Remove declare from the .ts file
4. The .ts file now contains only implementations with proper export statements
### TypeScript Version Considerations
This error handling has been consistent across TypeScript versions 3.x through 5.x. However, error messaging may vary slightly. Check your TypeScript version with:
tsc --versionIf upgrading TypeScript, re-run the compiler to get updated diagnostics.
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