Skip to content

[Bug]: preserveModules emits dangling export reference for TypeScript interface re-exported without type keyword #9141

@kendalblythe

Description

@kendalblythe

Reproduction link or steps

https://repl.rolldown.rs/ (or minimal repo below)

Source files:

src/types.ts:

export interface Foo {
  name: string;
}

src/impl.ts:

export class Bar {
  value = 42;
}

src/index.ts:

export { Foo } from './types';
export { Bar } from './impl';

Rolldown config:

module.exports = {
  input: { index: 'src/index.ts' },
  output: {
    dir: 'dist',
    preserveModules: true,
    preserveModulesRoot: 'src',
    sourcemap: true,
  },
  external: [],
};

What is expected?

Since Foo is a TypeScript interface (type-only, no runtime value), rolldown should fully elide it from the JS output — both the import and the export reference should be removed. The output dist/index.js should be:

import { Bar } from "./impl.js";
export { Bar };

This is how Rollup handles the same scenario.

What is actually happening?

Rolldown correctly strips the import of Foo (since there's no runtime binding to import), but leaves Foo in the export statement, producing:

import { Bar } from "./impl.js";
export { Bar, Foo };

Foo is never imported or defined, so this is invalid JavaScript. Downstream consumers (e.g., webpack) fail with:

Module parse failed: Export 'Foo' is not defined

System Info

rolldown: 1.0.0-rc.11
Node: v22.x
OS: Linux

Any additional comments?

Important notes

  • This only affects export { X } from './file' where X is a pure type (interface or type alias) and the type keyword is not used.
  • Using export type { Foo } from './types' works correctly — rolldown fully strips both the import and export.
  • The issue occurs with preserveModules: true. It may also occur without preserveModules but that was not tested.
  • TypeScript does not require the type keyword on re-exports of type-only symbols (unless isolatedModules + verbatimModuleSyntax are both enabled). Bundlers that process TypeScript (including Rollup, esbuild, and tsc) handle this correctly without the type keyword.

Workaround

Add the type keyword to re-exports of type-only symbols:

export type { Foo } from './types';  // add `type` keyword
export { Bar } from './impl';

Any additional comments?

Related but distinct from:

This bug is specifically about rolldown's TypeScript type elision being incomplete: it strips the import side of a type-only re-export but not the export side, producing invalid output.

Metadata

Metadata

Type

Priority

None yet

Effort

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions