This TypeScript error occurs when you try to re-export everything from a module using 'export * from' syntax without specifying what to export. The fix involves using named exports, namespace exports, or adjusting your module structure to properly re-export the desired content.
The "Re-exports require an export specifier" error (TS1205) appears when TypeScript encounters invalid re-export syntax. Re-exports allow you to export items from another module as if they were defined in the current module, but they must follow specific syntax rules. In TypeScript/ES modules, you can re-export in several ways: 1. **Named re-exports**: `export { name } from './module'` 2. **Namespace re-exports**: `export * as namespace from './module'` 3. **Default re-exports**: `export { default } from './module'` The error typically occurs when you try to use `export * from './module'` without any specifier, which is invalid syntax. You need to either export specific named exports or use the namespace export syntax. This error is common when refactoring code, creating barrel files (index.ts files that export from multiple modules), or when migrating from CommonJS to ES modules.
The most common cause is using export * from without proper syntax. Here's how to fix it:
// WRONG - missing export specifier
export * from './utils';
// CORRECT - export everything as a namespace
export * as utils from './utils';
// CORRECT - export specific named exports
export { functionA, functionB } from './utils';
// CORRECT - export default
export { default } from './utils';
// CORRECT - export everything (valid in some contexts)
export * from './utils/*'; // Note: this exports from multiple filesIf you want to export everything from a module, you have two options:
1. Use namespace export: export * as moduleName from './module'
2. Export specific items: export { item1, item2, default } from './module'
After fixing the syntax, compile again:
npx tsc
# or
npm run buildBarrel files (index.ts files that export from multiple modules) need proper syntax:
// WRONG barrel file
export * from './button';
export * from './input';
export * from './form';
// CORRECT barrel file - export specific items
export { Button, ButtonProps } from './button';
export { Input, InputProps } from './input';
export { Form, FormProps } from './form';
// CORRECT barrel file - namespace exports
export * as Button from './button';
export * as Input from './input';
export * as Form from './form';
// CORRECT barrel file - mixed approach
export { Button, ButtonProps } from './button';
export * as Input from './input';
export { default as Form } from './form';Best practices for barrel files:
- Export only what's needed (avoid export *)
- Use consistent patterns across your codebase
- Consider using export type for type-only exports
- Document what each barrel file exports
Example of a well-structured barrel file:
// src/components/index.ts
export { Button, type ButtonProps } from './Button';
export { Input, type InputProps } from './Input';
export { Card, type CardProps } from './Card';
export { default as Modal } from './Modal';If you're migrating from CommonJS to ES modules, re-exports work differently:
// CommonJS style (WRONG in ES modules)
module.exports = require('./utils');
// ES module equivalent (CORRECT)
export * from './utils'; // Still needs fixing!
export * as utils from './utils'; // Better
// CommonJS re-export pattern
exports.foo = require('./foo').foo;
exports.bar = require('./bar').bar;
// ES module equivalent
export { foo } from './foo';
export { bar } from './bar';
// For default exports
const utils = require('./utils');
module.exports = utils;
// ES module equivalent
export { default } from './utils';
// or
import utils from './utils';
export default utils;Update your package.json to specify module type:
{
"type": "module"
}And update tsconfig.json:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node16"
}
}Ensure your tsconfig.json supports the re-export patterns you're using:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node16",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"resolveJsonModule": true,
// For namespace exports
"preserveSymlinks": true,
// For better re-export support
"verbatimModuleSyntax": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}Key settings:
- module: Use ESNext for modern ES modules
- moduleResolution: node16 or bundler for proper ES module support
- allowSyntheticDefaultImports: Helps with default exports
- esModuleInterop: Improves compatibility with CommonJS modules
- verbatimModuleSyntax: Ensures imports/exports are preserved as written
After updating tsconfig.json, restart your TypeScript server:
# In VS Code: Ctrl/Cmd + Shift + P, then "TypeScript: Restart TS Server"If you're only exporting types, use type-only exports to avoid runtime issues:
// WRONG - exports both value and type
export { User, type UserProps } from './types';
// BETTER - separates type and value exports
export { User } from './types';
export type { UserProps } from './types';
// CORRECT - type-only re-export
export type { UserProps } from './types';
// For multiple type exports
export type { UserProps, PostProps, CommentProps } from './types';
// Namespace for types
export type * as Types from './types';Type-only exports are removed during compilation, so they don't affect the runtime code. This is especially important for:
1. Library authors: Consumers only get the types they need
2. Tree-shaking: Bundlers can remove unused type exports
3. Circular dependencies: Type-only imports don't create runtime dependencies
Check if you need import type instead of regular imports:
// Regular import (includes runtime code)
import { User } from './types';
// Type-only import (removed during compilation)
import type { UserProps } from './types';Some re-export patterns require newer TypeScript versions:
# Check current TypeScript version
npx tsc --version
# Update TypeScript
npm install typescript@latest --save-dev
# Or update to a specific version
npm install [email protected] --save-devNewer TypeScript versions support:
- export type * syntax (TypeScript 3.8+)
- export * as ns syntax (TypeScript 3.8+)
- Better ES module/CJS interop
- Improved error messages for re-exports
Check your package.json for compatibility:
{
"devDependencies": {
"typescript": "^5.0.0"
},
"engines": {
"node": ">=16.0.0"
}
}After updating, clear any cached files:
# Remove compiled files
rm -rf dist build
# Clear TypeScript cache
rm -rf node_modules/.cache
# Reinstall if needed
npm install
# Compile fresh
npx tsc --build --clean
npx tsc### Understanding Re-export Syntax Variations
TypeScript/JavaScript has several re-export patterns with different behaviors:
1. Named re-exports: export { a, b } from './module'
- Re-exports specific named exports
- Can rename: export { a as alpha, b as beta } from './module'
- Type-only: export type { T } from './module'
2. Namespace re-exports: export * as ns from './module' (TS 3.8+)
- Exports everything under a namespace
- Useful for organizing related exports
- Consumers import: import { ns } from './file'
3. Aggregating re-exports: export * from './module'
- Actually valid in some contexts!
- Exports all named exports (except default)
- Cannot be used at module top-level without specifier
- Works in specific patterns: export * from './folder/*'
4. Default re-exports: export { default } from './module'
- Re-exports the default export
- Alternative: import x from './module'; export default x;
### Barrel File Design Patterns
Well-designed barrel files improve developer experience:
// Pattern 1: Flat exports (simple)
export { Button } from './Button';
export { Input } from './Input';
export { Card } from './Card';
// Pattern 2: Namespaced (organized)
export * as Buttons from './buttons';
export * as Forms from './forms';
export * as Layout from './layout';
// Pattern 3: Mixed with utilities
export { Button, Input, Card } from './components';
export { formatDate, parseCurrency } from './utils';
export type { User, Product } from './types';
// Pattern 4: Lazy exports (for code splitting)
export { lazy } from 'react';
export const LazyButton = lazy(() => import('./Button'));### Module Resolution and Path Aliases
When using path aliases in tsconfig.json, re-exports need special handling:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@utils/*": ["utils/*"]
}
}
}Re-exports with aliases:
// Works in TypeScript
export { Button } from '@components/Button';
// May need runtime resolution
// Use a bundler (webpack, Vite) or module-alias package### Circular Dependencies and Re-exports
Re-exports can create circular dependencies. TypeScript handles these better than runtime:
// fileA.ts
export { b } from './fileB'; // Re-export from B
// fileB.ts
export { a } from './fileA'; // Re-export from A (circular!)
// Better: Use type-only imports/exports
export type { AType } from './fileA';### Tooling Support
Different tools handle re-exports differently:
- Bundlers (webpack, Rollup): May tree-shake unused re-exports
- Test runners (Jest): May need moduleNameMapper for path aliases
- IDEs (VS Code): Use "Go to Definition" on re-exports to find source
- Linters (ESLint): import/no-useless-path-segments rule checks re-exports
### Debugging Re-export Issues
Use TypeScript's compiler hints:
# Show module resolution
npx tsc --traceResolution
# Show all diagnostics
npx tsc --extendedDiagnostics
# Show import/export relationships
npx tsc --listFilesCheck what's actually being exported:
# For Node.js modules
node -e "console.log(Object.keys(require('./dist/index.js')))"
# For ES modules (Node.js with --experimental-modules)
node --loader ts-node/esm ./test-imports.mjsFunction 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