erasableSyntaxOnly asks TypeScript to reject syntax that needs JavaScript code generation. It is useful when Node.js runs .ts files by removing type annotations instead of performing a full TypeScript compilation.
Node’s built-in type stripping does not type-check your program and does not read tsconfig.json for runtime transformations. The flag helps the separate tsc check catch code that Node cannot erase safely.
Recommended tsconfig for Node type stripping
Node’s current TypeScript documentation recommends settings shaped like these:
{
"compilerOptions": {
"noEmit": true,
"target": "ESNext",
"module": "NodeNext",
"rewriteRelativeImportExtensions": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
}
}
Run both commands in CI:
npx tsc --noEmit
node src/server.ts
The first command checks types and syntax. The second proves that the real runtime can execute the entry point.
Erasable syntax vs transformed syntax
This type annotation can disappear without changing JavaScript behavior:
function double(value: number): number {
return value * 2;
}
After stripping types, ordinary JavaScript remains:
function double(value) {
return value * 2;
}
An enum is different because TypeScript normally generates a runtime object:
enum Status {
Pending,
Complete
}
Type stripping cannot remove the enum and preserve its behavior. Use a JavaScript value plus a derived type:
const Status = {
Pending: 'pending',
Complete: 'complete'
} as const;
type Status = typeof Status[keyof typeof Status];
Syntax the flag helps catch
| Syntax | Why it is a problem | Erasable alternative |
|---|---|---|
enum |
Requires a runtime object | as const object |
| Parameter property | Generates a class field assignment | Declare and assign the field |
namespace with values |
Generates runtime structure | ES modules and plain objects |
Import alias with = |
Requires transformation | Standard import syntax |
Parameter property before:
class UserService {
constructor(private repository: UserRepository) {}
}
Erasable version:
class UserService {
private repository: UserRepository;
constructor(repository: UserRepository) {
this.repository = repository;
}
}
The type annotation disappears, while the field assignment is already ordinary JavaScript.
Use type-only imports
When an import is used only as a type, mark it explicitly:
import type { User } from './user.ts';
import { saveUser, type SaveOptions } from './save-user.ts';
Without type, Node can treat the import as a runtime value import and fail because interfaces and type aliases do not exist at runtime. verbatimModuleSyntax helps TypeScript enforce this distinction.
Node does not apply path aliases
This can pass editor checks:
{
"compilerOptions": {
"paths": {
"@app/*": ["./src/*"]
}
}
}
But Node’s lightweight type stripping intentionally ignores tsconfig path transformations. Use relative imports or Node package subpath imports beginning with #.
{
"imports": {
"#app/*": "./src/*"
}
}
When to use erasableSyntaxOnly
Use it when:
- Node directly executes project scripts or a small service written in TypeScript.
- Your runtime strips types but does not transform TypeScript-only runtime syntax.
- You want the compiler to enforce a portable subset of TypeScript.
- Fast startup without a separate emit step is useful.
Do not adopt direct execution only because it removes one build command. A bundler or compiler is still the better fit when you need downlevel JavaScript, decorators, path rewriting, JSX transforms, bundling, or package output.
What the flag does not do
erasableSyntaxOnly does not:
- Type-check code at runtime.
- Make
pathsaliases work in Node. - Convert newer JavaScript for an older runtime.
- Bundle files or copy assets.
- Allow Node to execute TypeScript inside
node_modules. - Replace tests for the actual production command.
Migration checklist
- Add
erasableSyntaxOnlyand runtsc --noEmit. - Replace enums and parameter properties one category at a time.
- Mark type-only imports with
type. - Replace TypeScript-only aliases with runtime-supported imports.
- Run the real
.tsentry point under the production Node version. - Test scripts, workers, and test setup files, not only the web server.
The mental model is simple: if removing TypeScript syntax would require inventing new JavaScript, type stripping alone cannot run it.
Discussion
What would you try, change, or challenge after reading this guide? Specific results and errors help the next reader.
Comments will load as you reach this section.