Go to TypeScript transpiler
npm install goscript[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card] 
[GoDoc]: https://godoc.org/github.com/aperturerobotics/goscript
[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/goscript?status.svg
[Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/goscript
[Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/goscript
GoScript is an experimental Go to TypeScript compiler that translates Go code to TypeScript at the AST level. The goal is to enable sharing algorithms and business logic between Go backends and TypeScript frontends.
> Right now goscript looks pretty cool if you problem is "I want this self-sufficient algorithm be available in Go and JS runtimes". gopherjs's ambition, however, has always been "any valid Go program can run in a browser". There is a lot that goes on in gopherjs that is necessary for supporting the standard library, which goes beyond cross-language translation.
>
> — nevkontakte, developer of GopherJS
Write once, run everywhere. Share your Go algorithms, business logic, and data structures seamlessly between your backend and frontend without maintaining two codebases.
Use cases:
- Sharing business logic between Go services and web apps
- Porting Go algorithms to run in browsers
- Building TypeScript libraries from existing Go code
Go has powerful concurrency support and an excellent standard library. GoScript brings these capabilities to TypeScript with as simple and readable of a translation as possible.
ā What works:
- Structs, interfaces, methods, and functions with full value semantics
- Channels and goroutines (translated to async/await with function coloring)
- Pointers and addressability (via VarRef system)
- Slices, maps, and built-in types
- Control flow (if, for, switch, select, range, defer, etc.)
- Type assertions and interface implementations
- Closures and anonymous functions
- Generics
- Reflection
- Encoding: encoding/json
- Most of the standard library
š§ In progress:
- Reflection edge cases
- Full standard library coverage
- Various other edge cases (see GitHub issues)
Important Notes
- Uses JavaScript number type (64-bit float, not Go's int types)
- No pointer arithmetic (uintptr) or unsafe package
- No complex numbers
š Learn more: Design document | Architecture explainer | Compliance tests
š Found an issue? Please open an issue.
GoScript requires Bun to be installed for running compliance tests:
``bash`Install Bun
curl -fsSL https://bun.sh/install | bash
Option 1: Go Install
`bash`
go install github.com/aperturerobotics/goscript/cmd/goscript@latest
Option 2: NPM (if available)
`bash`
npm install -g goscript
`bash`Try compiling your Go package to TypeScript
goscript compile --package . --output ./dist
After compiling your Go code to TypeScript, you'll need to set up your project appropriately.
Create or update your tsconfig.json with these settings:
`json`
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "esnext.disposable", "dom"],
"baseUrl": "./",
"paths": {
"@goscript/": ["./path/to/generated/output/@goscript/"]
},
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true
}
}
Important requirements:
- target: "ES2022" or newer - Required for Disposable and other featureslib: ["esnext.disposable"]
- - Enables TypeScript's disposable types for resource managementbaseUrl
- and paths - Allows TypeScript to resolve @goscript/* importsmoduleResolution: "bundler"
- - Recommended for modern bundlers
You should be able to use any TypeScript bundler to compile the generated TypeScript.
`bash`
goscript compile --package ./my-go-code --output ./dist
Options:
- --package - Go package to compile (default: ".")--output
- - Output directory for TypeScript files
Go:
`go
import "github.com/aperturerobotics/goscript/compiler"
conf := &compiler.Config{OutputPath: "./dist"}
comp, err := compiler.NewCompiler(conf, logger, nil)
_, err = comp.CompilePackages(ctx, "your/package/path")
`
Node.js:
`typescript
import { compile } from 'goscript'
await compile({
pkg: './my-go-package',
output: './dist',
})
`
React + GoScript:
`typescript
import { NewCalculator } from '@goscript/myapp/calculator'
function CalculatorApp() {
const [calc] = useState(() => NewCalculator())
const handleAdd = () => {
const result = calc.Add(5, 3)
setResult(result)
}
return
}
`
Vue + GoScript:
`vue`
See the example/app for a full todo list application using GoScript with tRPC, Drizzle ORM, and React, or example/simple for a comprehensive demo of language features.
Go Code (user.go):
`go
package main
type User struct {
ID int json:"id"json:"name"
Name string json:"email"
Email string
}
func (u *User) IsValid() bool {
return u.Name != "" && u.Email != ""
}
func NewUser(id int, name, email string) *User {
return &User{ID: id, Name: name, Email: email}
}
func FindUserByEmail(users []User, email string) User {
for _, user := range users {
if user.Email == email {
return user
}
}
return nil
}
`
Compile it:
`bash`
goscript compile --package . --output ./dist
Generated TypeScript (user.gs.ts):
`typescript
export class User {
public ID: number = 0
public Name: string = ''
public Email: string = ''
public IsValid(): boolean {
const u = this
return u.Name !== '' && u.Email !== ''
}
constructor(init?: Partial
if (init) Object.assign(this, init)
}
}
export function NewUser(id: number, name: string, email: string): User {
return new User({ ID: id, Name: name, Email: email })
}
export function FindUserByEmail(users: User[], email: string): User | null {
for (let user of users) {
if (user.Email === email) {
return user
}
}
return null
}
`
Use in your frontend:
`typescript
import { NewUser, FindUserByEmail } from '@goscript/myapp/user'
// Same logic, now in TypeScript!
const users = [
NewUser(1, 'Alice', 'alice@example.com'),
NewUser(2, 'Bob', 'bob@example.com'),
]
const alice = FindUserByEmail(users, 'alice@example.com')
console.log(alice?.IsValid()) // true
`
Go Code:
`go
func ProcessMessages(messages []string) chan string {
results := make(chan string, len(messages))
for _, msg := range messages {
go func(m string) {
// Simulate processing
processed := "ā " + m
results <- processed
}(msg)
}
return results
}
`
Generated TypeScript:
`typescript
export function ProcessMessages(messages: string[]): $.Channel
let results = $.makeChannel
for (let msg of messages) {
queueMicrotask(async (m: string) => {
let processed = 'ā ' + m
await results.send(processed)
})(msg)
}
return results
}
`
Use with async/await:
`typescript
import { ProcessMessages } from '@goscript/myapp/processor'
async function handleMessages() {
const channel = ProcessMessages(['hello', 'world', 'goscript'])
// Receive processed messages
for (let i = 0; i < 3; i++) {
const result = await channel.receive()
console.log(result) // "ā hello", "ā world", "ā goscript"
}
}
``
- Try GoScript on your code and report issues
- Check the compliance tests for current progress
- Contribute test cases for edge cases you discover
MIT