How to Integrate typescript-go into an Existing Go Project: 3 Methods Explained

You can integrate typescript-go into an existing Go project by either running the tsgo CLI tool, embedding the compiler as a library via execute.CommandLine with a custom System implementation, or deploying the JSON-RPC/MessagePack API server for cross-process communication.

typescript-go is Microsoft's official TypeScript compiler rewritten in Go, offering full compatibility with existing tsconfig.json configurations while providing native Go APIs for programmatic control. Whether you need a drop-in replacement for tsc or a compiler embedded within your build pipeline, the repository provides three distinct integration paths based on the cmd/tsgo, internal/execute, and internal/api packages.

Installation and Module Configuration

Add the typescript-go module to your project using the standard Go toolchain:

go get github.com/microsoft/typescript-go@latest

This updates your go.mod to include the dependency:

module your/project

go 1.26

require github.com/microsoft/typescript-go v0.0.0-<commit-hash>

The module exposes both high-level CLI wrappers and low-level compiler primitives, allowing you to choose the appropriate abstraction level for your integration.

Method 1: Running the tsgo CLI Tool

The simplest integration method uses the cmd/tsgo binary as a drop-in replacement for the traditional TypeScript compiler. This approach requires minimal code changes and supports all standard tsc flags.

Build the CLI from source:

go build -o tsgo ./cmd/tsgo
./tsgo --project tsconfig.json

Under the hood, cmd/tsgo/main.go parses command-line arguments and delegates to execute.CommandLine in internal/execute/tsc/compile.go. This function orchestrates the entire compilation pipeline including configuration parsing, type checking, and file emission.

When to use this method: Choose the CLI approach when you need a standalone binary for build scripts or CI/CD pipelines without requiring programmatic access to compilation results.

Method 2: Embedding the Compiler as a Library

For custom build pipelines or IDE integrations, embed the compiler directly by implementing the tsc.System interface and invoking execute.CommandLine programmatically.

The interface definition resides in internal/execute/tsc/compile.go and requires methods for file system access, environment variables, and output writing. Reference the osSys type in cmd/tsgo/sys.go as a production-ready implementation template.

Create a minimal wrapper:

package tsintegration

import (
	"io"
	"os"
	"time"

	"github.com/microsoft/typescript-go/internal/bundled"
	"github.com/microsoft/typescript-go/internal/execute/tsc"
	"github.com/microsoft/typescript-go/internal/tspath"
	"github.com/microsoft/typescript-go/internal/vfs"
	"github.com/microsoft/typescript-go/internal/vfs/osvfs"
	"golang.org/x/term"
)

type System struct {
	writer             io.Writer
	fs                 vfs.FS
	defaultLibraryPath string
	cwd                string
	start              time.Time
}

func (s *System) Writer() io.Writer                { return s.writer }
func (s *System) FS() vfs.FS                        { return s.fs }
func (s *System) DefaultLibraryPath() string       { return s.defaultLibraryPath }
func (s *System) GetCurrentDirectory() string      { return s.cwd }
func (s *System) WriteOutputIsTTY() bool           { return term.IsTerminal(int(os.Stdout.Fd())) }
func (s *System) GetWidthOfTerminal() int          { return 80 }
func (s *System) GetEnvironmentVariable(n string) string { return os.Getenv(n) }
func (s *System) Now() time.Time                   { return time.Now() }
func (s *System) SinceStart() time.Duration        { return time.Since(s.start) }

func NewSystem() *System {
	cwd, _ := os.Getwd()
	return &System{
		writer:             os.Stdout,
		cwd:                tspath.NormalizePath(cwd),
		fs:                 bundled.WrapFS(osvfs.FS()),
		defaultLibraryPath: bundled.LibPath(),
		start:              time.Now(),
	}
}

// Compile executes the TypeScript compiler with the provided arguments.
func Compile(args []string) tsc.ExitStatus {
	sys := NewSystem()
	result := execute.CommandLine(sys, args, nil)
	return result.Status
}

Use the wrapper in your application:

package main

import (
	"fmt"
	"yourproject/tsintegration"
)

func main() {
	// Equivalent to: tsgo --project tsconfig.json --noEmit
	status := tsintegration.Compile([]string{"--project", "tsconfig.json", "--noEmit"})
	
	if status == 0 {
		fmt.Println("TypeScript compilation succeeded")
	} else {
		fmt.Printf("Compilation failed with exit code %d\n", status)
	}
}

The execute.CommandLine function handles the complete compilation lifecycle: parsing tsconfig.json, creating a compiler.Program, running type checking, and managing incremental builds or watch modes.

When to use this method: Choose library embedding when you need to capture compilation status codes, intercept diagnostics, or integrate TypeScript compilation into larger Go-based build systems.

Method 3: Deploying the JSON-RPC/MessagePack API Server

For language-server integrations or scenarios requiring process isolation, typescript-go provides a stdio-based API server via internal/api.

Initialize the server:

package main

import (
	"context"
	"os"
	"os/signal"
	"syscall"

	"github.com/microsoft/typescript-go/internal/api"
	"github.com/microsoft/typescript-go/internal/bundled"
)

func main() {
	opts := &api.StdioServerOptions{
		Err:                os.Stderr,
		Cwd:                ".",
		DefaultLibraryPath: bundled.LibPath(),
		Async:              false, // false = JSON-RPC, true = MessagePack
	}
	
	srv := api.NewStdioServer(opts)

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()

	if err := srv.Run(ctx); err != nil {
		panic(err)
	}
}

The server reads requests from stdin (or named pipes) and returns compilation results via the configured protocol. The cmd/tsgo/api.go file provides the reference implementation for launching this server from command-line contexts.

When to use this method: Choose the API server for VS Code extensions, long-running build daemons, or polyglot environments where Go code must communicate with TypeScript tooling written in other languages.

Integration Method Comparison

Select the appropriate integration strategy based on your architectural requirements:

  • CLI Tool: Use when replacing tsc in existing build scripts without code changes. Entry point: cmd/tsgo/main.go.
  • Library Embedding: Use for custom build orchestration in Go. Entry point: execute.CommandLine with a custom System implementation.
  • API Server: Use for IDE integrations or cross-language services. Entry point: api.NewStdioServer.

Summary

Integrating typescript-go into an existing Go project offers three distinct approaches depending on your isolation and control requirements:

  • Install via go get github.com/microsoft/typescript-go to add the module to your project
  • Run the CLI by building cmd/tsgo for drop-in tsc replacement functionality
  • Embed as a library by implementing the tsc.System interface and calling execute.CommandLine from internal/execute/tsc/compile.go
  • Deploy the API server using api.NewStdioServer from internal/api for JSON-RPC or MessagePack-based inter-process communication
  • Reference cmd/tsgo/sys.go for the canonical System implementation when embedding the compiler

Frequently Asked Questions

Can typescript-go use existing tsconfig.json files?

Yes. The execute.CommandLine function in internal/execute/tsc/compile.go fully parses and respects tsconfig.json configurations, including compiler options, file includes, and project references. Pass --project tsconfig.json in your arguments array to specify the configuration file path.

How does typescript-go handle the TypeScript standard library?

The compiler bundles standard library definitions (.d.ts files) internally via the internal/bundled package. When implementing the tsc.System interface, return bundled.LibPath() from the DefaultLibraryPath() method to provide access to these embedded type definitions without requiring external node_modules installations.

What is the performance difference between library embedding and the CLI?

Both methods use the same underlying compilation engine in internal/execute/tsc/compile.go. Library embedding eliminates process startup overhead and allows reusing vfs.FS instances across multiple compilations, potentially improving performance for incremental builds or watch mode implementations compared to spawning separate CLI processes.

Is the API server compatible with the Language Server Protocol (LSP)?

No. The internal/api server uses a custom JSON-RPC/MessagePack protocol specific to typescript-go's compiler operations, distinct from LSP. It exposes compilation and project management functions rather than editor-oriented features like hover information or go-to-definition. For LSP functionality, you would need to build a separate adapter layer that calls the typescript-go library programmatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →