TypeScript 6.0 Finally Answers the 'How Do I Run TypeScript?' Question
Source: typescript
For the past several years, answering ‘how do I run TypeScript?’ has required a follow-up question: in what context? Development server? Use ts-node or tsx. CI type checking? tsc —noEmit, probably. Bundled output? esbuild or swc, because tsc is too slow. Tests? ts-jest or vitest with a swc transform. Node.js scripts? Maybe a shebang and ts-node/esm. The question fragmented into a decision tree before you even wrote a line of code.
TypeScript 6.0 does not declare all of that obsolete. But it provides three interlocking tools that, together, make a coherent single-toolchain answer possible for the first time.
The Three Pieces
The first piece is the Go-based compiler. The TypeScript team has been building typescript-go in the open since early 2025, and TypeScript 6.0 ships it as the production tsc. The compiler checks the TypeScript codebase itself in roughly 1.5 seconds versus 15 seconds on the old JavaScript-based compiler. VS Code and other large projects see similar magnitude improvements. For the first time, using tsc for emit in a tight development loop is not obviously the wrong choice.
The second piece is --noCheck. This flag tells tsc to emit output without running type checking. That may sound like --transpileOnly from ts-node, or like using swc directly, but the distinction matters: this is the real tsc, with correct module resolution, correct declaration emit, and correct handling of every configuration option. The type checking is simply deferred.
The split-pipeline this enables looks like:
# In one terminal or CI step: type-check without emitting
tsc --noEmit
# In another: emit without type-checking
tsc --noCheck
For watch-mode development, you can have tsc --noCheck --watch producing output files instantly while tsc --noEmit --watch catches type errors in the background. Neither step requires swc, esbuild, or any third-party transpiler. You get the full TypeScript compiler behavior at a fraction of the latency.
The third piece is --erasableSyntaxOnly. This flag restricts your TypeScript to the subset that can be handled by a token stripper rather than a full compiler. When your code passes this check, Node.js can run it natively via --strip-types (stable since Node 24), Deno can run it, Bun can run it, and any stripping-based tool handles it correctly without a special transform pass.
Specifically, --erasableSyntaxOnly disallows:
enumdeclarations (which compile to IIFE-style runtime objects)namespaceandmoduledeclarations with runtime bodies- Parameter properties in constructors (
constructor(private name: string)becomesthis.name = namein output, which is not a strip) - Legacy
experimentalDecorators-style decorators
// Allowed: pure annotation, deleted cleanly
function connect(host: string, port: number): Promise<void> {
return openSocket(host, port);
}
// Disallowed: enum compiles to an object, not erased
enum Protocol {
HTTP,
HTTPS,
WS
}
// Use this instead: union type erases completely
type Protocol = 'http' | 'https' | 'ws';
// Disallowed: parameter property emits constructor body code
class Server {
constructor(private host: string, private port: number) {}
}
// Allowed: explicit property + constructor
class Server {
private host: string;
private port: number;
constructor(host: string, port: number) {
this.host = host;
this.port = port;
}
}
If your code passes --erasableSyntaxOnly, you have options that did not exist before. You can drop the build step in development and point Node.js directly at your .ts files. You can publish TypeScript source to npm and let consumers run it without configuring a bundler. You can test with node --test on .ts files without a Jest configuration.
How They Work Together
The reason these three features matter together rather than individually is that they address different parts of the fragmentation problem.
The Go compiler solves the performance reason to avoid tsc. Previously, tsc was slow enough that teams reached for swc or esbuild for emit, accepting the trade-off of different behavior in exchange for speed. With a 10x improvement on large codebases, that trade-off disappears for most projects. You can use tsc --noCheck in your dev server loop and tsc --noEmit for type checking without either step feeling like a tax.
--noCheck solves the behavioral reason to avoid tsc. Even on the rare team that did not mind the performance cost, using tsc for both type checking and emit in a single invocation means any type error blocks output. --noCheck separates those concerns without introducing a different toolchain. Your emit behavior matches your type-check behavior because it is the same compiler.
--erasableSyntaxOnly solves the interoperability reason to use a separate toolchain. If you have been reaching for swc or esbuild because Node.js’s native TypeScript support cannot handle your enums, --erasableSyntaxOnly tells you exactly what to change. Pass the flag, fix the errors, and the third-party transpiler becomes optional.
None of these features are compulsory. A project using TypeScript with complex decorators and enums and targeting older Node.js versions can continue doing exactly what it does today. But for a greenfield project, or a project doing an infrastructure review, TypeScript 6.0 makes the all-tsc path viable in a way it has not been before.
The Enum Migration Question
The practical sticking point for most existing codebases will be enums. They are pervasive in TypeScript code written in the 2018-2023 era, particularly in server-side and library code. The --erasableSyntaxOnly error messages are clear about what to change, but the migration requires judgment.
String literal unions replace most enum use cases cleanly:
// Before
enum LogLevel { Debug, Info, Warn, Error }
function log(level: LogLevel, message: string) { ... }
// After
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
function log(level: LogLevel, message: string) { ... }
The output serializes naturally, works across module boundaries without shared definitions, and requires no runtime support. For cases where you need the enum value to be opaque or where you are interoperating with external systems that expect numeric codes, const enum is still permitted under --erasableSyntaxOnly because it inlines at compile time and leaves no runtime artifact. That is a narrow use case, but it is there.
Parameter properties are more mechanical to migrate: expand each constructor(private foo: string) to an explicit property declaration plus a constructor assignment. The behavior is identical. A codemod handles most of this without manual work.
What This Does Not Solve
The Go compiler changes the TypeScript compiler API in ways that matter for tooling authors. Tools that use ts.createProgram, ts-morph, or custom compiler plugins reached into the internal transformation pipeline. The public API surface is maintained, but semi-public internals have changed or been removed. If you maintain tooling that uses the TypeScript compiler programmatically, testing against 6.0 before upgrading is worth doing before your users do it for you.
The performance gains also do not translate linearly to all project shapes. The 10x benchmark is on large codebases with dense type graphs. A small project with a simple configuration will see improvement but not a dramatic one. The Go compiler matters most at the scale where the old compiler was genuinely painful.
The Practical Takeaway
For a TypeScript project targeting Node 24+, a reasonable TypeScript 6.0 setup looks like:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"erasableSyntaxOnly": true,
"isolatedDeclarations": true,
"strict": true
}
}
With that configuration, tsc --noEmit gives you type checking and node --strip-types gives you execution, with no third-party transpiler anywhere in the path. For development, node --watch --strip-types src/index.ts replaces the ts-node watch loop. For libraries, source can be published directly and consumed without a compile step by any runtime that supports stripping.
This is not the only valid TypeScript setup in 2026. Projects with complex build requirements, custom transformations, or bundled output have different needs. But TypeScript 6.0 is the first version where the simple path is also a complete path, and that matters for the majority of TypeScript code that does not need the complex one.