Originally discovered in listr2 issue: listr2/listr2#748 (comment)
Also reported in rollup-plugin-dts: Swatinem/rollup-plugin-dts#354
Describe the bug
When using export * from './foo' in a source file, type-only exports from ./foo lose their type modifier in the bundled declaration output. This causes types to be emitted as value exports in the generated d.ts, which breaks with classes under verbatimModuleSyntax because they appear as runtime symbols.
index.ts
export * from "./interfaces";
interfaces.ts
export { type Task, type TaskWrapper } from "./tasks";
tasks.ts
export class TaskWrapper {
run(): void {}
}
export interface Task {
title: string;
}
Output (dist/index.d.ts):
//#region tasks.d.ts
declare class TaskWrapper {
run(): void;
}
interface Task {
title: string;
}
//#endregion
export { Task, TaskWrapper };
(no type keyword in TaskWrapper export)
Then, in a consumer, importing TaskWrapper with auto-import in VS Code will not use a type keyword and will not report any TS errors with verbatimModuleSyntax: true. Using Node.js type stripping also yields a syntax error:
consumer.ts
import { TaskWrapper } from "./dist/index.js";
$ node consumer.ts
file:///project/workspace/consumer.ts:1
import { TaskWrapper } from "./dist/index.js";
^^^^^^^^^^^
SyntaxError: The requested module './dist/index.js' does not provide an export named 'TaskWrapper'
at ModuleJob._instantiate (node:internal/modules/esm/module_job:228:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:337:5)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:651:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
Node.js v22.19.0
This led to a problem with listr2 because ListrTaskWrapper is not reported as an error with verbatimModuleSyntax: true, even though it's actually a type-only export:
import { ListrTask /* ✅ error as expected with `verbatimModuleSyntax: true` */ } from 'listr2';
import { ListrTaskWrapper, /* 💥 no error as expected with `verbatimModuleSyntax: true` */ } from 'listr2';
TypeScript Playground
rolldown-plugin-dts should emit a type modifier for the export when there has been export type { ... } or export { type ... } used somewhere along with export *
Reproduction
https://codesandbox.io/p/devbox/hzmx8h
System Info
System:
OS: Linux 6.1 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
CPU: (4) x64 AMD EPYC
Memory: 6.40 GB / 8.01 GB
Container: Yes
Shell: Unknown
Binaries:
Node: 22.19.0 - /usr/local/bin/node
Yarn: 1.22.22 - /usr/local/bin/yarn
npm: 10.9.3 - /usr/local/bin/npm
Used Package Manager
npm
Validations
Contributions
Originally discovered in
listr2issue: listr2/listr2#748 (comment)Also reported in rollup-plugin-dts: Swatinem/rollup-plugin-dts#354
Describe the bug
When using
export * from './foo'in a source file, type-only exports from./foolose theirtypemodifier in the bundled declaration output. This causes types to be emitted as value exports in the generatedd.ts, which breaks with classes underverbatimModuleSyntaxbecause they appear as runtime symbols.index.tsinterfaces.tstasks.tsOutput (
dist/index.d.ts):(no
typekeyword inTaskWrapperexport)Then, in a consumer, importing
TaskWrapperwith auto-import in VS Code will not use atypekeyword and will not report any TS errors withverbatimModuleSyntax: true. Using Node.js type stripping also yields a syntax error:consumer.ts$ node consumer.ts file:///project/workspace/consumer.ts:1 import { TaskWrapper } from "./dist/index.js"; ^^^^^^^^^^^ SyntaxError: The requested module './dist/index.js' does not provide an export named 'TaskWrapper' at ModuleJob._instantiate (node:internal/modules/esm/module_job:228:21) at async ModuleJob.run (node:internal/modules/esm/module_job:337:5) at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:651:26) at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) Node.js v22.19.0This led to a problem with
listr2becauseListrTaskWrapperis not reported as an error withverbatimModuleSyntax: true, even though it's actually a type-only export:TypeScript Playground
rolldown-plugin-dtsshould emit atypemodifier for the export when there has beenexport type { ... }orexport { type ... }used somewhere along withexport *Reproduction
https://codesandbox.io/p/devbox/hzmx8h
System Info
System: OS: Linux 6.1 Debian GNU/Linux 11 (bullseye) 11 (bullseye) CPU: (4) x64 AMD EPYC Memory: 6.40 GB / 8.01 GB Container: Yes Shell: Unknown Binaries: Node: 22.19.0 - /usr/local/bin/node Yarn: 1.22.22 - /usr/local/bin/yarn npm: 10.9.3 - /usr/local/bin/npmUsed Package Manager
npm
Validations
Contributions