This TypeScript error occurs when generating declaration files (.d.ts) for code that exports a type or interface that hasn't been explicitly exported from the module. The compiler cannot create valid type declarations when the default export references private (unexported) types.
The "Default export of the module has or is using private name" error (TS4082) appears when TypeScript's declaration file generator encounters a default export that references a type, interface, or class that is not publicly exported from the module. This happens specifically when you have declaration file generation enabled (via "declaration": true in tsconfig.json). TypeScript's declaration emitter needs to serialize all types used in public exports into .d.ts files that other developers can use. When it encounters a private type in an export signature, it cannot safely emit that type because: 1. The type isn't imported/exported in the declaration file 2. Adding automatic imports would expose implementation details the developer didn't intend to make public 3. The resulting declaration file would be invalid or incomplete This error is particularly common in: - Vue components using `defineProps<Props>()` where Props isn't exported - Default exports of functions/classes that use internal types in their signatures - Libraries attempting to export functionality that depends on private types - Projects using frameworks that generate type definitions at build time
The most straightforward fix is to explicitly export the type that's being referenced:
// BEFORE - Causes TS4082 error
interface Props {
name: string;
age: number;
}
export default defineComponent({
props: defineProps<Props>()
});
// AFTER - Fixed by exporting Props
export interface Props {
name: string;
age: number;
}
export default defineComponent({
props: defineProps<Props>()
});For React components:
// BEFORE - Error when building library
interface ButtonProps {
variant: 'primary' | 'secondary';
onClick: () => void;
}
export default function Button(props: ButtonProps) {
return <button onClick={props.onClick}>{props.children}</button>;
}
// AFTER - Fixed
export interface ButtonProps {
variant: 'primary' | 'secondary';
onClick: () => void;
}
export default function Button(props: ButtonProps) {
return <button onClick={props.onClick}>{props.children}</button>;
}This makes the type part of your public API, allowing TypeScript to include it in generated declarations.
If you don't want to export the type, define it inline directly in the export:
// Instead of a separate interface
interface Props {
title: string;
count: number;
}
export default defineProps<Props>();
// Use inline type definition
export default defineProps<{
title: string;
count: number;
}>();For function exports:
// BEFORE - Private Config type
interface Config {
apiKey: string;
timeout: number;
}
export default function initialize(config: Config) {
// ...
}
// AFTER - Inline type
export default function initialize(config: {
apiKey: string;
timeout: number;
}) {
// ...
}This approach keeps your module's public API simple while avoiding the private name error.
If certain files shouldn't produce type declarations (like tests, stories, or examples), exclude them:
Update tsconfig.json:
{
"compilerOptions": {
"declaration": true
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.stories.ts",
"**/__tests__/**"
]
}For build tools like Rollup with @rollup/plugin-typescript:
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
export default {
plugins: [
typescript({
exclude: [
'**/*.test.ts',
'**/*.spec.ts',
'**/*.stories.tsx',
'src/__tests__/**'
]
})
]
};This prevents TypeScript from trying to generate declarations for files that might use private types liberally.
Use explicit type annotations to mask the private type from the declaration emitter:
// BEFORE - Private Test class leaks into export
class Test {
constructor(private value: string) {}
}
export const instance = new Test("value");
// Error: Exported variable 'instance' has or is using private name 'Test'
// AFTER - Explicit 'any' type annotation
export const instance: any = new Test("value");
// BETTER - Define a public interface
export interface Instance {
getValue(): string;
}
class Test implements Instance {
constructor(private value: string) {}
getValue() { return this.value; }
}
export const instance: Instance = new Test("value");For functions with complex return types:
// Private internal type
type InternalResult = {
_internal: string;
data: string;
};
// Hide implementation details with explicit return type
export default function process(): { data: string } {
const result: InternalResult = {
_internal: "secret",
data: "public"
};
return result;
}Converting to named exports can sometimes avoid the issue and is generally considered best practice:
// BEFORE - Default export with private type
interface ComponentProps {
title: string;
}
export default function Component(props: ComponentProps) {
return <div>{props.title}</div>;
}
// AFTER - Named exports (recommended)
export interface ComponentProps {
title: string;
}
export function Component(props: ComponentProps) {
return <div>{props.title}</div>;
}Named exports are easier for tree-shaking, refactoring, and avoiding this specific error class. They also make the module's public API more explicit.
Usage changes from:
import Component from './Component';To:
import { Component } from './Component';As a last resort for debugging, disable declaration file generation to verify this is the issue:
// tsconfig.json
{
"compilerOptions": {
"declaration": false,
// Or temporarily:
// "declaration": true,
// "declarationMap": false
}
}Warning: This is not a proper fix if you're building a library that others will consume. Declaration files are essential for TypeScript library users to get type checking and autocomplete.
Use this approach only to:
- Confirm the error is related to declaration generation
- Unblock development while planning proper fix
- Build applications (not libraries) where .d.ts files aren't distributed
### Understanding Declaration File Generation
TypeScript's declaration emitter follows strict rules to ensure generated .d.ts files are valid:
1. No implementation details: Declaration files should only contain type information, not runtime code
2. All referenced types must be resolvable: Any type appearing in a declaration must be importable by consumers
3. Private types cannot be serialized: The emitter won't automatically export types you didn't intend to share
When you write:
interface InternalConfig {
secret: string;
}
export default function init(config: InternalConfig) {
// ...
}The emitter tries to generate:
// Would be invalid - InternalConfig is not exported!
export default function init(config: InternalConfig): void;This creates an invalid declaration file because InternalConfig isn't defined or imported.
### Vue-Specific Considerations
Vue 3's defineProps<T>() is a compile-time macro that extracts type information. When used with private types:
// Problematic pattern
interface Props {
msg: string;
}
export default defineComponent({
setup() {
const props = defineProps<Props>();
}
});Solutions for Vue:
1. Export the Props interface:
export interface Props {
msg: string;
}2. Use inline types:
const props = defineProps<{
msg: string;
}>();3. Use runtime props definition (loses some type safety):
const props = defineProps({
msg: { type: String, required: true }
});### Library Development Best Practices
When building TypeScript libraries:
1. Design your public API intentionally: Decide which types should be exported
2. Use index.ts barrel exports: Explicitly control what's public:
// src/index.ts
export { Button, type ButtonProps } from './Button';
export { Input, type InputProps } from './Input';
// Don't export internal utilities3. Consider API extractors: Tools like [@microsoft/api-extractor](https://api-extractor.com/) can help manage declaration files and detect private type leaks
4. Document exported types: If a type is exported for declaration file purposes, document whether it's meant for external use
### Third-Party Type Issues
Sometimes the error comes from dependencies:
// Your code
import { SomeFunction } from 'third-party-lib';
export default SomeFunction;
// Error: Default export uses private name from 'third-party-lib'Fixes:
1. Update the dependency - may be fixed in newer version
2. Report the issue to the library maintainer
3. Create a wrapper with explicit types:
import { SomeFunction } from 'third-party-lib';
export default function wrapped(...args: any[]): any {
return SomeFunction(...args);
}### Composite Projects and Project References
In monorepos using TypeScript project references, ensure referenced projects have proper exports:
// packages/shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true
}
}// packages/shared/src/index.ts
// Export all types that dependent packages might need
export interface Config { ... }
export type Status = 'active' | 'inactive';### Debugging Declaration Output
To see what TypeScript is trying to generate:
# Generate declarations and inspect output
npx tsc --declaration --emitDeclarationOnly --outDir dist-types
# Review the generated .d.ts files
cat dist-types/yourfile.d.tsThis shows exactly what the emitter is struggling with.
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