GoScript: a Go to TypeScript compiler
Your Go code runs anywhere TypeScript runs: Node, Bun, and the browser.
GoScript compiles Go packages into readable TypeScript modules. Goroutines,
channels, select, defer, pointers, and struct copies behave as they do in
Go, and the output is ordinary TypeScript you can import, bundle, and step
through in a debugger.
go install github.com/s4wave/goscript/cmd/goscript@latest
goscript compile --package . --output ./output
Install
GoScript needs the Go toolchain. Install the CLI with Go:
go install github.com/s4wave/goscript/cmd/goscript@latest
Or add it to a JavaScript project. The npm package runs the same compiler
through your local Go toolchain and adds the TypeScript API:
bun add -d goscript
Bun runs generated code directly. For Node and browsers,
bundle it with a bundler that resolves tsconfig.json paths, such as Bun,
Vite, or esbuild.
Quick Start
Write a Go program:
// main.go
package main
import "fmt"
type Greeter struct{ Name string }
func (g Greeter) Greet() string { return "Hello, " + g.Name + "!" }
func main() {
ch := make(chan string)
go func() { ch <- Greeter{Name: "GoScript"}.Greet() }()
fmt.Println(<-ch)
}
Compile it from the module directory. GoScript also emits the packages it
imports, here fmt, so the output runs on its own:
goscript compile --package . --output ./output
Point @goscript/* imports at the output in tsconfig.json:
{
"compilerOptions": {
"paths": { "@goscript/*": ["./output/@goscript/*"] }
}
}
Run it:
$ bun run output/@goscript/example.com/hello/main.gs.ts
Hello, GoScript!
Each Go package becomes a directory under output/@goscript/, named by its
import path, with one .gs.ts file per Go file and an index.ts that exports
the package. A package main runs directly; any other package is a module you
import:
import { NewUser } from '@goscript/example.com/my/module/index.js'
docs/typescript.md has the full tsconfig.json for
typechecking and bundling generated code.
Usage
Compile packages
goscript compile --package ./pkg/... --output ./output
--package takes any Go package pattern and repeats. GoScript compiles the
requested packages and every package they import. --skip-dependencies emits
only the requested packages and the runtime, for builds that compile
dependencies separately.
docs/cli.md lists every option.
Run Go tests
goscript test compiles a package's Go tests to TypeScript and runs them with
Bun, or in Chromium with --browser. The output follows go test:
goscript test --tags goscript ./...
Compile from TypeScript
import { compile } from 'goscript'
await compile({
pkg: '.',
output: './output',
dir: process.cwd(),
})
Compile from Go
comp, err := compiler.NewCompiler(&compiler.Config{
Dir: ".",
OutputPath: "./output",
}, nil, nil)
if err != nil {
return err
}
_, err = comp.CompilePackages(ctx, ".")
Compile in the browser
github.com/s4wave/goscript/compiler/wasm builds to WebAssembly and compiles a
single Go source file to TypeScript in the page. It accepts files without
imports; the website playground uses it.
ts, err := wasm.CompileSource(src, "main")
Features
- Go language: structs, interfaces, generics, closures, slices, maps, and value copies.
- Pointers:
&x, pointers to pointers, and pointer receivers behave as in Go.
- Concurrency: goroutines, channels,
select, sync, defer, panic, and recover.
- Exact integers:
int64 and uint64 compile to bigint and wrap like Go.
- Control flow:
goto, labels, type switches, and range over iterator functions.
- Standard library:
fmt, strings, sync, time, encoding/json, crypto, and more.
- Third-party packages: go-git, klauspost/compress, blake3, protobuf-go-lite, and more.
- Go tests:
goscript test runs your Go tests on the output in Bun or Chromium.
- Large programs: GoScript compiles Spacewave's browser core, including go-git.
- In the browser: the compiler runs in the page through WebAssembly.
How It Works
Go packages -> type check -> semantic model -> lowered IR -> TypeScript
+ runtime + overrides
GoScript loads packages with the Go toolchain and type-checks them. It then
decides which variables need a pointer box, which functions must become
async, and how each type maps to TypeScript, before it writes any text. The
emitter renders the result, and the compiler copies the
@goscript/builtin runtime and any handwritten
overrides for packages such as sync, os, and reflect.
A function that can block on a channel, select, or a lock becomes async,
and so does every function that calls it. Goroutines run as async tasks on the
JavaScript event loop, and no WebAssembly runtime ships with your code.
docs/explainer.md walks through each stage with
generated output for structs, pointers, channels, and defer.
Limitations
- The CLI and APIs take package patterns, not individual
.go files.
- Browser compilation accepts single files without imports. Compile code with
imports through the CLI or API.
unsafe type-checks, but Sizeof, Alignof, Offsetof, and pointer
conversions throw at runtime. Pointer arithmetic and cgo are unsupported.
int, uint, and integers narrower than 64 bits are JavaScript numbers.
uint and uintptr keep full 64-bit width, but int does not wrap on
64-bit overflow.
- A standard-library package works when GoScript ships an override for it or
it compiles cleanly from Go. Sockets, processes, and plugin loading work only
as far as the JavaScript host supports them.
reflect covers types, values, struct fields, maps, MakeFunc, FuncOf,
and DeepEqual, but not all of the package.
goscript test supports a subset of testing and of the go test flags.
- Async calls and
bigint arithmetic cost more than synchronous JavaScript on
plain numbers.
Why GoScript
Use GoScript when Go is the source of truth and part of your product runs in a
TypeScript runtime: shared validation and business rules, TypeScript packages
published from Go code, or Go framework code running in the browser without a
rewrite.
GoScript is built against Spacewave, a
large Go and TypeScript application framework. Spacewave compiles its browser
core plugin through GoScript, including go-git and the go-mysql-server SQL
engine, and runs its core package tests through goscript test in CI.
GopherJS shares the goal of running Go
in JavaScript and ships its own goroutine scheduler. GoScript emits readable
TypeScript modules and maps goroutines onto JavaScript async functions.
Development
bun install
bun run test
bun run lint
bun run build
bun run example compiles and runs example/simple.
bun run website:build builds the website and playground.
example/app is a full-stack application built on generated
TypeScript.
The compliance tests under tests/tests
are Go programs compiled, typechecked, and run against expected output.
Contributing
To fix a missing Go behavior, add a focused compiler or compliance test that
reproduces it, then implement the behavior in the compiler or runtime stage
responsible for it.
Open an issue for Go code GoScript cannot compile, runtime gaps, and missing
standard-library overrides.
License
MIT