By default tsc performs import elision: it drops an import (or a named binding) it sees is never used as a value. That works because tsc has the whole program and knows whether User is an interface or a class. A single-file transpiler (Babel, ts.transpileModule, esbuild-style tools) cannot see the other file, so it cannot make that call. The three features exist for that gap.
import type/export type(TS 3.8). The declaration is usable only in type positions and is erased completely. Using it as a value is an error (TS1361), soclass X extends Importedfails if you imported withimport type. The inline formimport { type A, b }(TS 4.5) marks single names.isolatedModules. A checking flag. It reports code that cannot be compiled from one file alone, for example re-exporting a type withoutexport type(TS1205). Per the docs it "does not change the behavior" of checking and emitting. It does not turn off elision.verbatimModuleSyntax(TS 5.0). Removes the guesswork: an import or export withouttypeis kept; anything withtypeis dropped. If you import a type withouttype, you get TS1484. It replacesimportsNotUsedAsValuesandpreserveValueImports, which TS 5.9 reports as removed.
Consequences of verbatim emit:
import { type A } from "./m"becomesimport {} from "./m", which keeps the side effects of./m. Useimport type { A }to drop the module entirely.- It forbids ESM syntax in a file that compiles to CommonJS (TS1287); use
import x = require()andexport =there.
Recommendation: enable verbatimModuleSyntax in new projects, because TS 5.9's tsc --init template already does (alongside isolatedModules), and your emit then matches what Node, Babel, or a bundler will do with the source.