This TypeScript error (TS4023) occurs when generating declaration files and the compiler cannot properly reference types from external modules in your exported variables. It happens when you export a variable whose inferred type uses types from external modules that cannot be explicitly named in the generated .d.ts file.
The TS4023 error appears when TypeScript is generating declaration files (enabled via `declaration: true` or `composite: true` in tsconfig.json) and encounters an exported variable that uses types from external modules in a way that cannot be properly represented in the generated .d.ts file. TypeScript needs to create a public type signature for every exported value. When you export a variable whose type is inferred from external module types, the compiler must decide how to express that type in the declaration file. If the type includes references to types that aren't explicitly imported or can't be publicly named, TypeScript cannot generate a valid declaration file. This error is fundamentally about the boundary between your module's public API and its internal implementation. TypeScript is warning you that consumers of your module won't be able to see accurate type information for this exported variable. Common scenarios include: 1. **Inferred types from re-exports**: Exporting objects or arrays that contain instances of classes from external modules 2. **Complex type inference**: TypeScript infers a type that includes private or non-exported types from dependencies 3. **Declaration file generation**: The error only appears when TypeScript tries to generate .d.ts files, not during regular compilation In TypeScript 2.9+, many cases are handled automatically using import type syntax in generated declarations, but complex scenarios still trigger this error.
The most direct solution is to explicitly type your exported variables instead of relying on type inference:
// BEFORE - Type is inferred, causes TS4023
import { DataGrid } from '@mui/x-data-grid';
export const myGrid = DataGrid({ /* config */ });
// Error: Exported variable 'myGrid' has or is using name 'DataGridComponent'
// AFTER - Explicitly typed
import { DataGrid, DataGridProps } from '@mui/x-data-grid';
export const myGrid: React.ComponentType<DataGridProps> = DataGrid({ /* config */ });For complex objects:
// BEFORE
import { someFunction } from 'external-lib';
export const config = {
handler: someFunction(),
options: { /* ... */ }
};
// AFTER
import { someFunction, HandlerType } from 'external-lib';
interface ConfigType {
handler: HandlerType;
options: { /* ... */ };
}
export const config: ConfigType = {
handler: someFunction(),
options: { /* ... */ }
};This gives TypeScript explicit type information to include in the declaration file.
Sometimes the error occurs because TypeScript knows about a type but hasn't imported it in your file. Explicitly import the type mentioned in the error:
// Error message says:
// "has or is using name 'NumberKeywords' from external module 'ajv/dist/types/json-schema'"
// BEFORE - Type used but not imported
import Ajv from 'ajv';
export const validator = new Ajv();
// AFTER - Import the type explicitly
import Ajv from 'ajv';
import type { NumberKeywords } from 'ajv/dist/types/json-schema';
export const validator = new Ajv();Even if you don't reference NumberKeywords directly in your code, importing it allows TypeScript to reference it in the generated declaration file.
You can also use type imports to avoid runtime overhead:
import type { SomeType } from 'external-module';
import { someFunction } from 'external-module';
export const result: ReturnType<typeof someFunction> = someFunction();If you're re-exporting types or objects from external modules, use explicit type exports:
// BEFORE - Re-exporting without types
export { default as MyComponent } from 'external-lib';
// AFTER - Explicit type export
export type { ComponentProps } from 'external-lib';
export { default as MyComponent } from 'external-lib';For aggregating exports:
// BEFORE
import * as Utils from './utils';
export const helpers = Utils;
// AFTER
export type * as Utils from './utils';
export * from './utils';
// OR with explicit namespace
import type * as UtilsType from './utils';
import * as UtilsImpl from './utils';
export const helpers: typeof UtilsType = UtilsImpl;If your exported variable uses a complex type, ensure all parts of that type are properly exported from their source modules:
// external-lib/index.ts
// BEFORE - InternalConfig not exported
interface InternalConfig {
setting: string;
}
export function createHandler(config: InternalConfig) {
return { /* ... */ };
}
// AFTER - Export all types used in public API
export interface InternalConfig {
setting: string;
}
export function createHandler(config: InternalConfig) {
return { /* ... */ };
}In your consuming code:
import { createHandler } from 'external-lib';
import type { InternalConfig } from 'external-lib';
export const handler: ReturnType<typeof createHandler> = createHandler({
setting: 'value'
});TypeScript 2.9 introduced better handling of this error by supporting import types in declaration files:
# Check your TypeScript version
npx tsc --version
# Update to latest TypeScript
npm install --save-dev typescript@latest
# Or specific version (5.x recommended)
npm install --save-dev typescript@^5.0.0After updating, TypeScript will automatically use import("module").Type syntax in generated declarations:
// Your source code
import { Hash } from 'crypto';
export const hash = createHash('sha256');
// Generated declaration (TypeScript 2.9+)
export declare const hash: import("crypto").Hash;This resolves many cases of TS4023 without requiring manual type annotations. Check your tsconfig.json to ensure you're using modern compiler options:
{
"compilerOptions": {
"target": "ES2020",
"module": "esnext",
"moduleResolution": "bundler",
"declaration": true
}
}For variables whose types are based on function returns or complex expressions, use TypeScript utility types:
// BEFORE
import { createComplexObject } from 'external-lib';
export const instance = createComplexObject({ /* config */ });
// AFTER - Using ReturnType
import { createComplexObject } from 'external-lib';
export const instance: ReturnType<typeof createComplexObject> =
createComplexObject({ /* config */ });For class instances:
// BEFORE
import { SomeClass } from 'external-lib';
export const instance = new SomeClass();
// AFTER - Using InstanceType
import { SomeClass } from 'external-lib';
export const instance: InstanceType<typeof SomeClass> = new SomeClass();For objects with known shape:
import { processData } from 'external-lib';
type ProcessedData = {
result: string;
metadata: Record<string, unknown>;
};
export const data: ProcessedData = processData(input);### Understanding Declaration File Generation
When TypeScript generates declaration files, it must create a complete public API description without implementation details. The compiler faces challenges when:
1. Visibility boundaries: A public export references a type that isn't public
2. Module boundaries: Types cross module boundaries in complex ways
3. Inference chains: Type inference creates long chains through multiple modules
### Project References and Composite Mode
This error is particularly common in monorepos using project references (composite: true):
// tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true
}
}Composite projects require perfect declaration files for cross-project type checking. Solutions:
1. Explicit API boundaries: Define clear interfaces for cross-package communication
2. Barrel exports: Use index.ts files to re-export with explicit types
3. Type-only dependencies: Use import type for type-only references
### Module Detection and Type Imports
TypeScript 4.7+ introduced moduleDetection which affects how files are treated:
{
"compilerOptions": {
"moduleDetection": "force"
}
}This forces all files to be modules, requiring explicit imports/exports. This can help by making type dependencies explicit.
### Working with Third-Party Libraries
Some libraries have imperfect type definitions. Workarounds:
Option 1: Augment module declarations
// types/module-augmentation.d.ts
import 'problematic-library';
declare module 'problematic-library' {
export interface PublicConfig {
// Add missing types
}
}Option 2: Create wrapper functions
// Instead of exporting library objects directly
import { ThirdPartyClass } from 'library';
// Create a typed wrapper
export interface MyWrapperConfig {
// Your explicit config type
}
export function createWrapper(config: MyWrapperConfig) {
return new ThirdPartyClass(config);
}### Declaration Reference Directives
For complex multi-package scenarios, use triple-slash directives:
/// <reference types="dependency-package" />
export const value: DependencyType = createValue();This ensures TypeScript includes necessary type information in declaration output.
### Debugging Declaration Generation
See what TypeScript is generating:
# Generate declarations and inspect
npx tsc --declaration --emitDeclarationOnly
# Check the .d.ts file
cat dist/index.d.tsLook for:
- Missing type imports
- any types (red flag for lost type information)
- Incomplete type signatures
### Performance Implications
Explicit type annotations have benefits beyond fixing TS4023:
1. Faster type checking: Compiler doesn't need to infer complex types
2. Better error messages: Errors appear at declaration site, not call site
3. Intentional API design: Forces you to think about your public API
4. Easier refactoring: Type changes show immediate impact
### Alternative: Disable Declaration Generation Temporarily
If you're building an application (not a library), you may not need declarations:
{
"compilerOptions": {
"declaration": false
}
}However, this prevents using project references and can impact IDE performance in large projects.
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