TypeScript Best Practices

Chapter 1 · Practice 4 of 6

Write erasable TypeScript and mark type-only imports

From Compiler settings and TypeScript 7

Why it matters

More tools now run TypeScript by removing the types rather than compiling them. Node.js strips types by default since v22.18.0 and v23.6.0, and the feature is stable from v24.12.0 and v25.2.0. Type stripping replaces type annotations with whitespace. It does not type check, and it cannot handle syntax that generates JavaScript.

Node's docs list what fails under type stripping:

  • enum declarations
  • namespace blocks with runtime code
  • parameter properties such as constructor(private db: Db)
  • import x = require() aliases
  • paths from tsconfig, which Node ignores

Two compiler options keep your code compatible:

  • erasableSyntaxOnly reports an error for any of the syntax above.
  • verbatimModuleSyntax requires import type for imports used only as types, so a stripper never keeps an import of something that does not exist at runtime.

Even if you use a bundler today, erasable code is simpler to move between tools. Replace enums with as const objects or string literal unions, and write constructor assignments out in full.

Do

  • Turn on erasableSyntaxOnly and verbatimModuleSyntax in new projects
  • Use import type or inline type imports for anything used only as a type
  • Replace enum with a string literal union or an as const object

Avoid

  • Parameter properties like constructor(private db: Db) in code Node runs directly
  • Relying on tsconfig paths at runtime: Node ignores them

Example

TypeScript
// Erasable: works with Node type stripping and every bundler
import type { User } from './user.ts';

export const Role = { Admin: 'admin', Member: 'member' } as const;
export type Role = (typeof Role)[keyof typeof Role];

export class UserService {
  private readonly users: User[];
  constructor(users: User[]) {
    this.users = users;
  }
}