Native Type Stripping

Point node at a .ts file and it runs. No flag, no loader, no dist directory.

greet.ts and main.ts — note the .ts extension in the importTypeScript
// greet.ts
export interface User { id: number; name: string; roles?: string[] }
export function greet(u: User): string {
  return `${u.name} (#${u.id}) has ${(u.roles ?? []).length} role(s)`;
}
// main.ts
import { greet, type User } from './greet.ts';
const ada: User = { id: 1, name: 'Ada', roles: ['admin'] };
console.log(greet(ada), process.features.typescript);   // Ada (#1) has 1 role(s) strip

process.features.typescript is the runtime's own answer to "am I stripping?": the string 'strip' when stripping is active, false when it is off.

Node compiles nothing. It parses the file and overwrites every TypeScript-only construct with spaces — the experimental stripTypeScriptTypes export of node:module shows the result, turning const n: number = 1; into const n = 1;. Because each annotation becomes blanks of the same width, line and column numbers match the file on disk: a throw on source line 7, column 31 reports at assertPositive (file:///C:/work/trace.ts:7:31), honest in a way a transpile-and-run loader with a stale cache is not.

What Node does to a .ts file before V8 ever sees it
What Node does to a .ts file before V8 86,723 ever sees it

Stripping is a syntactic transform with no project model behind it, and that produces four hard rules.

To prove a build really emitted JavaScript, pass --no-strip-types, renamed from --no-experimental-strip-types in Node 25.2.0; a .ts file then fails with ERR_UNKNOWN_FILE_EXTENSION.