Some configurations automatically erase an import used only as a type, even without import type. With verbatimModuleSyntax: true, however, the spelling also changed whether a module's side effect ran. Check type-checking success separately from runtime module evaluation.
In particular, these forms do not always emit the same JavaScript:
import type { Ticket } from "./registry.js"import { type Ticket } from "./registry.js"
Put a visible marker in the imported module
The experiment ran on September 11, 2026, with macOS arm64, Node.js 24.15.0, TypeScript 7.0.2, and esbuild 0.28.2. Compiler options were strict: true, target: ES2022, module: ESNext, and moduleResolution: Bundler. esbuild targeted Node ESM with bundling and tree shaking enabled. The test package did not declare sideEffects: false.
registry.ts contains only a type and a log showing that the module was evaluated. It performs no external requests or real application initialization.
export interface Ticket { title: string }
console.log("registry evaluated");
The caller starts with this code:
import { Ticket } from "./registry.js";
const ticket: Ticket = { title: "Fix timer" };
console.log(ticket.title);
Only the import was changed when comparing emitted JavaScript and runtime logs. In the personal repository, the type-sharing package exposes API types through export type. The Ticket and log here are a minimal example designed to separate type references from runtime loading.
Both spelling and configuration affected the result
In this table, "evaluation" means the log printed registry evaluated. Every successful example also printed the caller's Fix timer message.
| Import form | verbatim | tsc | Registry evaluation in tsc output | Registry evaluation in esbuild bundle |
|---|---|---|---|---|
import { Ticket } |
false | Pass | No | No |
import type { Ticket } |
false | Pass | No | No |
import { type Ticket } |
false | Pass | No | No |
Side-effect import + import type
|
false | Pass | Yes | Yes |
import { Ticket } |
true | TS1484 | Emit stopped | Yes |
import type { Ticket } |
true | Pass | No | No |
import { type Ticket } |
true | Pass | Yes | Yes |
Side-effect import + import type
|
true | Pass | Yes | Yes |
The test used noEmitOnError: true, so no tsc-generated JavaScript was executed for the TS1484 row. esbuild bundled that input successfully, but that does not mean it passed type checking.
An inline type modifier left an empty import
This is the tsc output for import { type Ticket } with verbatimModuleSyntax: true:
import {} from "./registry.js";
const ticket = { title: "Fix timer" };
console.log(ticket.title);
The name Ticket disappeared, but import {} from "./registry.js" remained. That dependency still evaluates the module, so the marker was printed. Making the entire statement type-only with import type { Ticket } removed the statement and the log. The official verbatimModuleSyntax documentation also distinguishes these outputs.
When only a type is needed, use a whole-statement import type. When initialization is also required, make that a separate import:
import "./registry.js";
import type { Ticket } from "./registry.js";
const ticket: Ticket = { title: "Fix timer" };
console.log(ticket.title);
This printed the marker under both settings and both tools tested here. That does not establish the same outcome for dependencies marked as free of side effects or for other bundler configurations. This experiment observes two local files.
Successful bundling does not establish type correctness
export const count: number = "wrong";
console.log(typeof count);
tsc reported TS2322, while esbuild succeeded and the bundle printed string. The esbuild TypeScript caveats explain that TypeScript's type checker is responsible for checking the annotations.
Marking dependencies as type-only and running type checking in the build process address different concerns. In this reproduction, an input could run after import removal while still containing a mistake tsc detected. Bundle success alone did not catch that mistake.
Run the T06 cases in the comparison inputs with the execution script to inspect the combined diagnostics, emitted JavaScript, and logs. When introducing the setting into an application, inspect type-only references and imports intended for initialization separately.
Top comments (0)