May 22, 2025·6 min read·engineeringtypescriptcompilerperformancetooling

TypeScript native previews make compiler compatibility testable

Microsoft's Go-based compiler preview is now available through npm and a VS Code extension. JSX checking and JavaScript analysis broaden its usefulness, while missing build and editor features keep readiness specific to each workflow.

A compiler port becomes much easier to evaluate when application maintainers can install it beside their existing compiler. Building the implementation from source is useful for contributors, but it makes routine compatibility feedback harder to obtain. TypeScript's native preview now has a distribution path that fits the ecosystem it is intended to serve.

The May 22 announcement makes @typescript/native-preview available through npm. It provides tsgo, a separate executable that accepts familiar compiler arguments. A VS Code extension also exposes the developing native language service. Both remain previews with substantial missing functionality.

The separate executable matters because the current compiler remains available. A project can examine the native implementation's behavior without assuming that its entire build and editing workflow already has a replacement. That is a useful structure for a migration with several independent integration points.

Checking is further along than complete build support

The March port announcement established the plan to move the compiler and tools to Go while preserving TypeScript behavior closely. The May update says most of the checker has now been ported, with most projects expected to see the same errors apart from intentional changes and some stale standard-library definitions.

This is a meaningful claim about semantic coverage, but it does not cover every operation exposed by the stable compiler. Declaration emit and --build are still unavailable in this preview. Some older downlevel output targets are also missing.

A project that only needs a no-emit check can therefore have a different readiness assessment from a library that publishes declarations. Project references introduce another dependency because the compiler must coordinate output and dependencies across multiple configurations.

The distinction can be expressed in a small invocation. The preview accepts a project configuration and can check it without requesting output.

npx tsgo --project ./src/tsconfig.json --noEmit

That command is a checking entry point, not evidence that all referenced projects can be built through the preview. The announcement says existing tsc can still build dependencies whose declarations the preview language service may consume.

I value TypeScript's ecosystem more than I enjoy the language itself. That makes side-by-side adoption particularly important. A compiler transition should accommodate the actual responsibilities a framework or package build gives the compiler, rather than treating one successful check as proof that every integration is ready.

JSX now receives semantic analysis

The initial native implementation could parse JSX without fully checking it. That limited what early performance results could establish about applications using JSX. Parsing a syntax form and validating its relationships to types are different stages.

The May preview adds JSX checking. The announcement's Sentry comparison reports a stable-compiler check taking about 72.8 seconds and a native-preview run taking about 6.8 seconds on the same machine. Its authors note discrepancies and describes minimal project changes for the comparison.

Those are published measurements from the compiler team. They demonstrate that a substantial JSX project can exercise more of the checker while retaining a large speed improvement. They do not establish a universal timing or complete parity for every configuration.

The distinction is useful for evaluating the evidence. A faster program that skips an important source construct has performed less semantic work. Adding JSX checking makes the result more relevant to the web applications that use TypeScript extensively.

JSX output is a separate capability. The preview can preserve the JSX source form, but its emit support does not yet cover every transformation an application might expect. A project whose bundler handles JSX may have a different requirement from one relying on the compiler to transform it.

JavaScript checking takes a different migration approach

The preview also adds JavaScript and JSDoc analysis. The update says this support was rewritten instead of ported mechanically. Previously, the implementation accumulated recognition of particular JavaScript patterns over time, some of which the team considers uncommon or outdated.

That is a deliberate exception to the overall strategy. The porting rationale explains why preserving detailed checker behavior is normally central. Popular libraries depend on inference choices and implementation limits, so a fresh checker design could create broad incompatibility.

JavaScript analysis now accepts a more explicit compatibility tradeoff. Some source may need changes or more modern patterns. The announcement asks users to report difficulties rather than claiming that existing JavaScript checking behavior is already reproduced exactly.

This affects mixed-language codebases. A project may have TypeScript source that behaves consistently while JavaScript dependencies or internal JSDoc-annotated modules receive different analysis. Describing the whole project as TypeScript can hide that part of its checking workload.

It also provides a concrete category for feedback. A diagnostic difference caused by an intentional analysis change needs a different discussion from a missing checker operation. Reporting the source language and pattern helps identify which kind of difference a maintainer has found.

Editor support has its own completion criteria

The new VS Code extension requires explicit enablement after installation. The documented setting is typescript.experimental.useTsgo, and the command palette can enable or disable the preview. Installing the extension alone does not mean the native service has taken over.

The May update lists diagnostics, hover, and go-to-definition in early form, with completions newly enabled. Auto-imports, find-all-references, rename, and signature help remain work in progress. These differences matter because an editor workflow can depend heavily on operations beyond checking.

A developer who spends much of the day navigating code might accept a different subset from one who relies on automated rename across a project. The preview's usefulness is therefore specific to the work being performed. Calling the language service complete would conceal those dependencies.

The implementation now uses Language Server Protocol, while the existing service communicates through the older TSServer format. The team explains that this prevents a perfect mechanical translation of the editor layer. Messages and definitions need deliberate adaptation to the new protocol.

That work is separate from porting type relationships. A checker can compute an answer correctly while the editor adapter still lacks an operation for requesting or presenting it. The distribution of compiler and editor previews together makes both kinds of progress visible without equating them.

Module configuration can create intentional differences

The announcement identifies older Node resolution settings among the configurations that can produce differences in the native preview. It points toward node16, nodenext, or bundler models, depending on the project.

This is not a reason to choose an arbitrary setting merely to remove a diagnostic. Module configuration describes how the project expects imports to resolve and execute. A bundler-owned application and directly executed Node source have different environments.

A diagnostic comparison therefore needs to record configuration as well as compiler version. If one compiler invocation models a different module environment, the resulting errors do not isolate checker parity. The input contract changed too.

My preference is for a migration to expose these assumptions explicitly. TypeScript rewards careful adherence to framework conventions, and module configuration is part of those conventions. A faster implementation is most useful when the project can also state which environment the implementation should model.

API consumers cross a process boundary

The update describes an initial API layer over standard I/O, with JavaScript clients able to communicate with the compiler process. A native Node module called libsyncrpc, implemented in Rust, supports synchronous communication required by many existing integrations.

This follows the March API discussion, which warned that copying every existing in-process operation across a process boundary would be impractical. Call overhead and serialization affect how much benefit an integration receives from faster checking.

A tool that reads individual compiler objects repeatedly may need a more deliberate operation than a remote version of every property access. Batching can reduce communication overhead, but its behavior still needs to match the consumer's task. The API remains early design work.

That leaves tool authors with a separate readiness question from application authors invoking tsgo. A source project can check correctly while a linter, transform, or code generator still needs an integration path. The preview's availability makes the checking question easier to answer without declaring the API question settled.

The May release supplies a practical comparison target, with broader checker coverage and an explicitly limited editor experience. Its separate package and executable preserve the existing compiler while users exercise the native implementation. Feedback can now identify concrete semantic differences and missing workflow capabilities against source that maintainers already understand.