Running the new Go-based TypeScript compiler on a real monorepo

I spend an embarrassing amount of my life staring at a terminal waiting for TypeScript to finish thinking. On our main frontend monorepo, a cold type check takes about four minutes. That is enough time to check Slack, get distracted, and completely lose my train of thought. It kills momentum.

So when Microsoft pushed the @typescript/native-preview package last week, I dropped my current ticket immediately. A native TypeScript compiler written in Go. No Node.js overhead. Just raw binary speed. The official package name is @typescript/native-preview@7.0.0-dev.20260301.1, and it exposes a new CLI command: tsgo.

I wanted to see what happened when I threw a deeply nested, heavily generic React codebase at it. Not a synthetic benchmark. Real, messy production code.

The speed difference is absurd

I ran this on my M3 Max MacBook Pro running Sonoma 14.3. Our project relies heavily on complex mapped types and conditional type inference—the exact stuff that usually makes the standard tsc process choke and spin up your laptop fans.

I deleted the tsconfig.tsbuildinfo files to force a completely cold run.

Golang logo - Golang Logo Lapel Pin – Ardan Labs Swag Store
Golang logo – Golang Logo Lapel Pin – Ardan Labs Swag Store

With standard Node-based tsc, the check took 4 minutes and 12 seconds. The Node process peaked at 4.2GB of RAM usage before finally exiting.

I swapped the command to npx tsgo --build.

It finished in 28 seconds. Memory usage never crossed 1.4GB. I actually ran it three more times because I assumed it was failing silently or skipping project references. It wasn’t. It successfully caught the intentional type error I hid deep in a utility folder. The --incremental and --build flags work exactly as you expect them to.

To understand why this matters, look at the kind of code that usually slows down the checker. We write a lot of strictly typed asynchronous API wrappers that infer return types based on generic inputs.

type ApiResponse<T> = {
  data: T;
  status: number;
  timestamp: string;
};

// Complex type inference that usually slows down the Node-based compiler
// when scaled across hundreds of endpoints
async function fetchUserData<T extends { id: string }>(
  endpoint: string,
  userId: string
): Promise<ApiResponse<T>> {
  const res = await fetch(${endpoint}/users/${userId});
  
  if (!res.ok) {
    throw new Error(HTTP Error: ${res.status});
  }

  const rawData = await res.json();
  
  return {
    data: rawData as T,
    status: res.status,
    timestamp: new Date().toISOString()
  };
}

When you have thousands of these generic inferences chained together across a monorepo, the V8 JavaScript engine spends a massive amount of time just doing garbage collection. The Go implementation handles this memory allocation entirely differently, which explains the massive drop in RAM usage.

Where it falls apart

There is a massive catch. You cannot actually use this for your daily local development workflow — not yet, at least.

Golang logo - Go - Golang Logo Png - Free Transparent PNG Clipart Images Download
Golang logo – Go – Golang Logo Png – Free Transparent PNG Clipart Images Download

The compiler is fast, but the Language Server Protocol (LSP) implementation is missing. If you write TypeScript in VS Code, you rely on the LSP for everything. Auto-imports, finding all references, renaming variables across files, hovering to see a type definition. None of that is wired up to the Go binary yet.

Watch mode is also incomplete. You can’t just run tsgo --watch and have it instantly recompile your DOM manipulation scripts when you hit save.

// You still need the standard TS server for local dev 
// to get auto-complete on DOM events like this
function bindUserEvents(containerId: string) {
  const container = document.getElementById(containerId);
  if (!container) return;

  container.addEventListener('click', async (e: MouseEvent) => {
    const target = e.target as HTMLElement;
    
    if (target.matches('.btn-fetch')) {
      target.setAttribute('aria-busy', 'true');
      
      try {
        const user = await fetchUserData<{id: string, name: string}>('/api/v1', 'u-492');
        
        // Updating the DOM with strictly typed data
        const nameDisplay = document.querySelector('.user-name');
        if (nameDisplay) {
          nameDisplay.textContent = user.data.name;
        }
      } catch (err) {
        console.error('Failed to fetch user', err);
      } finally {
        target.removeAttribute('aria-busy');
      }
    }
  });
}

Without watch mode and LSP support, tsgo is practically useless for the actual act of writing code.

How I’m actually using it

Golang logo - Go Gopher - Go Programming Language Logo - Free Transparent PNG ...
Golang logo – Go Gopher – Go Programming Language Logo – Free Transparent PNG …

I didn’t abandon the tool just because local dev is broken. I moved it to the cloud.

I hooked tsgo into our CI/CD pipeline. I replaced the standard tsc --build step in our GitHub Actions workflow. Our automated PR checks went from taking 8 minutes down to just under 2 minutes. We run dozens of PR checks a day across a team of fourteen engineers. That single line change in our YAML file is going to save us a ridiculous amount of GitHub Actions compute minutes this month.

The setup is stupidly simple. You just install it as a dev dependency and swap your build script.

npm i -D @typescript/native-preview
# Then in your CI script:
npx tsgo --build tsconfig.json

Local development still runs on the standard Node-based TS server. The developers don’t notice any difference in their editors, but their pull requests get approved and merged significantly faster.

I expect the TypeScript team will have the LSP and watch mode fully ported by Q1 2027. Until then, keeping tsgo strictly in the CI pipeline is the smartest way to use it.

TypeScript 7 Release Notes TypeScript Compiler Source Code TypeScript Native Preview Tracking Issue

Elara Vance

Learn More →

Leave a Reply

Your email address will not be published. Required fields are marked *