GoScript is an experimental Go-to-TypeScript transpiler that converts Go source code into maintainable, idiomatic TypeScript while preserving Go's semantics. This document provides a deep dive into how GoScript works.
- Overview
- High-Level Architecture
- Compilation Pipeline
- Compiler Components
- Analysis Phase
- Code Generation Phase
- Type System Translation
- Runtime System
- Concurrency Model
- Value Semantics
GoScript translates Go code at the AST (Abstract Syntax Tree) level, producing readable TypeScript that preserves Go's type safety and semantics. The primary use case is sharing business logic between Go backends and TypeScript frontends.
- AST Mapping: Close mapping between Go AST and TypeScript output
- Type Preservation: Maintain Go's static typing in TypeScript
- Value Semantics: Emulate Go's value copying behavior for structs
- Idiomatic Output: Generate TypeScript that feels natural to TS developers
- Readability: Prioritize clear, understandable generated code
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GoScript Compile Service β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Public βββββΆβ Compile βββββΆβ Package β β
β β Adapters β β Request β β Graph β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β TypeScript ββββββ TypeScript ββββββ Lowered β β
β β Output β β Emitter β β Program β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β² β² β² β
β β β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Override β β Runtime β β Semantic β β
β β Registry β β Contract β β Model β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β @goscript/builtin Runtime β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β varRef.ts β slice.ts β channel.ts β map.ts β type.ts β defer.ts β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The public CLI uses github.com/aperturerobotics/cli and constructs
compiler.Config from command-local flag state. The public Go API is
compiler.Compiler, which forwards package patterns into CompileService.
The WASM/browser adapter parses and type-checks import-free single-file source
strings, builds the same semantic model used by package compilation, and then
reuses the v2 lowering and TypeScript emitter. Browser source imports still
return a structured diagnostic; use the package workflow for imported code.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMPILATION STATE MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββ
β START β
ββββββ¬βββββ
β
βΌ
βββββββββββββββββββββ
β VALIDATE REQUEST β
β β
β - package patternsβ
β - output path β
β - module root β
β - build flags β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β LOAD PACKAGE GRAPHβ Uses golang.org/x/tools/go/packages
β β Env: GOOS=js, GOARCH=wasm
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β SEMANTIC MODEL β
β β
β - package facts β
β - addressability β
β - methods/types β
β - async facts β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β OVERRIDE COPY PLANβ Discover handwritten gs/ packages
β β and validate dependencies before output
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β LOWER PROGRAM β Convert Go AST + semantic facts into
β β compiler-owned IR
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β EMIT TYPESCRIPT β Render only from lowered IR
β β and generate indexes
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β COPY OVERRIDES β Copy @goscript/builtin and required
β β handwritten packages
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββ
β END β
βββββββββββ
GoScript v2 uses owner-level components instead of a package/file compiler hierarchy. Each owner hides one durable rule boundary:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PUBLIC ADAPTERS β
β cmd/goscript: github.com/aperturerobotics/cli flag surface β
β compiler.Compiler: Go API adapter over CompileService β
β compiler/wasm: browser diagnostic adapter β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CompileService β
β - Coordinates request, graph, semantic, lowering, emit, copy β
β - Accumulates structured diagnostics β
β - Stops before writing output when owner diagnostics are errors β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββ ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β CompileRequestOwner β β PackageGraphOwner β β SemanticModelOwner β
β Normalizes adapters β β Loads packages β β Computes package, β
β and validates module β β with go/packages β β type, method, async, β
β package requests β β and build flags β β and address facts β
ββββββββββββββββββββββββ ββββββββββββββββββββββββ ββββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββ ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β OverrideRegistryOwnerβ β LoweringOwner β β TypeScriptEmitOwner β
β Discovers/copies gs/ β β Converts semantic β β Renders deterministicβ
β packages and async β β model + AST to IR β β TypeScript from IR β
β override metadata β β β β only β
ββββββββββββββββββββββββ ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RuntimeContractOwner β
β Owns generated helper names and @goscript/builtin import policy β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The main design constraint is that text emission is the last step. Earlier
owners decide package identity, imports, async coloring, pointer/value shape,
interface descriptors, generic dictionaries, override dependencies, and runtime
helper names before TypeScriptEmitOwner writes files.
The analysis phase pre-computes all information needed for code generation. This happens before any TypeScript is written.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ANALYSIS STATE MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββ
β START β
ββββββ¬βββββ
β
βΌ
βββββββββββββββββββββββ
β PROCESS IMPORTS β
β β
β Collect import β
β statements and β
β their usage β
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β ANALYZE FUNCTIONS β
β β
β For each function: β
β β’ Track receivers β
β β’ Named returns β
β β’ Closure captures β
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β VARIABLE USAGE β β Rules for NeedsVarRef: β
β ANALYSIS ββββββΆβ β’ Address taken (&var) β
β β β β’ Assigned to pointer β
β Determine which β β β’ Passed to function taking pointer β
β vars need VarRef β βββββββββββββββββββββββββββββββββββββββ
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β ASYNC ANALYSIS β β Async Roots (Inherently Async): β
β (Function Coloring) ββββββΆβ β’ Channel receive: <-ch β
β β β β’ Channel send: ch <- val β
β Propagate async β β β’ select statements β
β status through β β β’ go statements (goroutines) β
β call graph β β β
βββββββββββ¬ββββββββββββ β Propagation: β
β β β’ Calls async func β becomes async β
β βββββββββββββββββββββββββββββββββββββββ
βΌ
βββββββββββββββββββββββ
β DEFER ANALYSIS β
β β
β Mark blocks with β
β defer statements β
β (sync vs async) β
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β INTERFACE IMPL β
β TRACKING β
β β
β Map structs to β
β interfaces they β
β implement β
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β BUILD ANALYSIS β
β RESULT β
β β
β Package into β
β read-only struct β
βββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββ
β END β
ββββββββββ
type Analysis struct {
// Variable reference tracking
VariableUsage map[types.Object]*VariableUsage
// Function metadata
FunctionData map[types.Object]*FunctionInfo
MethodAsyncStatus map[string]bool
// Per-node metadata
NodeData map[ast.Node]*NodeInfo
// Interface implementations
InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
// Import management
SyntheticImportsPerFile map[string]map[string]*ImportInfo
ReferencedTypesPerFile map[string]map[types.Type]bool
// Comment preservation
Cmap ast.CommentMap
}After analysis, the compiler traverses the AST and generates TypeScript.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CODE GENERATION STATE MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββ
β START β
ββββββ¬βββββ
β
βΌ
βββββββββββββββββββββββ
β WRITE IMPORTS β
β β
β import * as $ from β
β "@goscript/builtin" β
β β
β Auto-imports from β
β same package β
βββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β FOR EACH DECL βββββββββββββββββββββββββββββββββββββ
β β β
β β’ GenDecl (type, β β
β const, var) β β
β β’ FuncDecl β β
βββββββββββ¬ββββββββββββ β
β β
βββββββΆ TYPE SPEC βββββββΆ WriteTypeSpec β
β (struct, interface, alias) β
β β
βββββββΆ VALUE SPEC ββββββΆ WriteValueSpec β
β (const, var) β
β β
βββββββΆ FUNC DECL βββββββΆ WriteFuncDecl β
β (function, method) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββ
β
β more decls?
β
βΌ
ββββββββββ
β END β
ββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EXPRESSION TRANSLATION FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
WriteValueExpr(expr)
β
βββββββββββββββββΌββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββ ββββββββββββββ ββββββββββββββ
β BasicLit β β Ident β β CallExpr β
β β β β β β
β "hello" β β varName β β func() β
β 42 β β β β β
β true β β Check if β β Check if β
βββββββ¬βββββββ β needs β β async, β
β β .value β β builtin, β
β β access β β type conv β
βΌ βββββββ¬βββββββ βββββββ¬βββββββ
ββββββββββββββ β β
β Write β βΌ βΌ
β literal β ββββββββββββββ ββββββββββββββ
ββββββββββββββ β WriteIdent β β WriteCall β
β β β Expr β
β Add .value β β β
β if VarRef β β Add await β
ββββββββββββββ β if async β
ββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββ ββββββββββββββ ββββββββββββββ
β BinaryExpr β β UnaryExpr β β IndexExpr β
β β β β β β
β a + b β β &x, *p β β arr[i] β
β x == y β β -n, !b β β map[key] β
βββββββ¬βββββββ βββββββ¬βββββββ βββββββ¬βββββββ
β β β
βΌ βΌ βΌ
Write left Handle addr/ Write collection
op right deref with with index
VarRef logic
| Go Type | TypeScript Type | Notes |
|---|---|---|
int, int32, int64 |
number |
JavaScript number |
float64, float32 |
number |
IEEE 754 64-bit |
string |
string |
Direct mapping |
bool |
boolean |
Direct mapping |
rune |
number |
Unicode code point |
byte |
number |
Byte value |
error |
$.error | null |
Runtime interface |
[]T |
T[] | null |
With __capacity |
map[K]V |
Map<K, V> | null |
Standard Map |
chan T |
$.Channel<T> |
Runtime class |
*T |
T | null or $.VarRef<T> | null |
Depends on addressability |
interface{} |
any |
Or specific interface |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STRUCT TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Go Source:
βββββββββββββββββββββββββββββββββββββββ
β type Person struct { β
β Name string β
β Age int β
β addr *Address β
β } β
βββββββββββββββββββββββββββββββββββββββ
β
β Translation
βΌ
TypeScript Output:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β export class Person { β
β // Getters/setters for clean API β
β public get Name(): string { β
β return this._fields.Name.value β
β } β
β public set Name(value: string) { β
β this._fields.Name.value = value β
β } β
β public get Age(): number { β
β return this._fields.Age.value β
β } β
β public set Age(value: number) { β
β this._fields.Age.value = value β
β } β
β public get addr(): Address | null { β
β return this._fields.addr.value β
β } β
β public set addr(value: Address | null) { β
β this._fields.addr.value = value β
β } β
β β
β // Internal storage with VarRefs for addressability β
β public _fields: { β
β Name: $.VarRef<string> β
β Age: $.VarRef<number> β
β addr: $.VarRef<Address | null> β
β } β
β β
β constructor(init?: Partial<{Name?: string, Age?: number, ...}>) { β
β this._fields = { β
β Name: $.varRef(init?.Name ?? ""), β
β Age: $.varRef(init?.Age ?? 0), β
β addr: $.varRef(init?.addr ?? null) β
β } β
β } β
β β
β // Clone for value semantics β
β public clone(): Person { β
β const cloned = new Person() β
β cloned._fields = { β
β Name: $.varRef(this._fields.Name.value), β
β Age: $.varRef(this._fields.Age.value), β
β addr: $.varRef(this._fields.addr.value) β
β } β
β return cloned β
β } β
β } β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The @goscript/builtin runtime provides essential helpers:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β @goscript/builtin β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β varRef.ts β β slice.ts β β channel.ts β β map.ts β β
β β β β β β β β β β
β β VarRef<T> β β makeSlice β β Channel<T> β β makeMap β β
β β varRef() β β slice() β β makeChannel β β mapSet β β
β β unref() β β append() β β selectStmt β β mapGet β β
β βββββββββββββββ β copy() β β chanSend β β deleteMap β β
β β len() β β chanRecv β β Entry β β
β β cap() β βββββββββββββββ βββββββββββββββ β
β βββββββββββββββ β
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β type.ts β β defer.ts β β errors.ts β β builtin.ts β β
β β β β β β β β β β
β β registerTypeβ β Disposable β β error type β β println β β
β β typeAssert β β Stack β β panic β β print β β
β β TypeInfo β β AsyncDisp. β β recover β β bitwise ops β β
β β TypeKind β β Stack β β β β int(), byte β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The VarRef system enables Go's pointer semantics in TypeScript:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VARREF SYSTEM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Go Code:
ββββββββββββββββββββββββββββ
β var x int = 10 β
β p := &x β
β *p = 20 β
β println(x) // 20 β
ββββββββββββββββββββββββββββ
β Analysis determines x needs VarRef (address taken)
βΌ
TypeScript Output:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β let x: $.VarRef<number> = $.varRef(10) // x is wrapped in VarRef β
β let p: $.VarRef<number> | null = x // p points to same VarRef β
β p!.value = 20 // modify through pointer β
β $.println(x.value) // access value: 20 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Memory Model Visualization:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β x ββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββ β
β β VarRef<number>β β
β β βββββββββββββ β β
β β β value: 20 β β β
β β βββββββββββββ β β
β βββββββββββββββββββ β
β β² β
β β β
β p ββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
GoScript translates Go's concurrency to TypeScript async/await:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FUNCTION COLORING ALGORITHM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Phase 1: Identify Async Roots
βββββββββββββββββββββββββββββ
ββββββββββββββββββ
β Scan all funcs β
βββββββββ¬βββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Contains any of these? βββββΆ Mark as ASYNC β
β β
β β’ <-ch (channel receive) β
β β’ ch <- val (channel send) β
β β’ select {} (select statement) β
β β’ go func() (goroutine creation) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Phase 2: Propagate Async Status
βββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β func A() { func B() { func C() { β
β <-ch βββ ASYNC A() βββ ASYNC B() βββ ASYNC β
β } } } β
β β
β Async propagates through call graph β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Phase 3: Code Generation
ββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β // SYNC function // ASYNC function β
β function add(a, b) { async function recv(ch) { β
β return a + b return await ch.receive() β
β } } β
β β
β // Call site // Call site β
β let sum = add(1, 2) let val = await recv(ch) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CHANNEL TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Go Code: TypeScript Output:
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β ch := make(chan int, 1) β ββββΆ β let ch = $.makeChannel<number>(1, 0)β
β β β β
β ch <- 42 β ββββΆ β await $.chanSend(ch, 42) β
β β β β
β val := <-ch β ββββΆ β let val = await $.chanRecv(ch) β
β β β β
β val, ok := <-ch β ββββΆ β let {value: val, ok} = β
β β β await $.chanRecvWithOk(ch) β
β β β β
β close(ch) β ββββΆ β ch.close() β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
Select Statement:
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β select { β β await $.selectStatement([ β
β case val := <-ch1: β ββββΆ β { β
β process(val) β β id: 0, isSend: false, β
β case ch2 <- data: β β channel: ch1, β
β sent() β β onSelected: async (r) => { β
β default: β β let val = r.value β
β nothing() β β process(val) β
β } β β } β
β β β }, β
β β β { β
β β β id: 1, isSend: true, β
β β β channel: ch2, value: data, β
β β β onSelected: async () => sent() β
β β β } β
β β β ], true) β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
Go Code: TypeScript Output:
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β go func() { β β queueMicrotask(async () => { β
β doWork() β ββββΆ β { β
β }() β β doWork() β
β β β } β
β β β }) β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
GoScript preserves Go's value semantics for structs:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VALUE SEMANTICS TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Go Code:
ββββββββββββββββββββββββββββββββββββββ
β original := Point{X: 10, Y: 20} β
β copy := original β // Creates independent copy
β copy.X = 100 β
β println(original.X) β // Still 10
ββββββββββββββββββββββββββββββββββββββ
β
βΌ
TypeScript Output:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β let original = new Point({X: 10, Y: 20}) β
β let copy = original.clone() // .clone() creates deep copy β
β copy.X = 100 β
β $.println(original.X) // Still 10 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Clone Implementation:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β public clone(): Point { β
β const cloned = new Point() β
β cloned._fields = { β
β X: $.varRef(this._fields.X.value), // Copy value β
β Y: $.varRef(this._fields.Y.value) // Copy value β
β } β
β return cloned β
β } β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FOR LOOP TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Standard For:
Go: for i := 0; i < 10; i++ { }
TS: for (let i = 0; i < 10; i++) { }
While Loop:
Go: for condition { }
TS: while (condition) { }
Infinite Loop:
Go: for { }
TS: for (;;) { }
For-Range (Slice):
Go: for i, v := range slice { }
TS: for (const [i, v] of $.rangeSlice(slice)) { }
// rangeSlice captures slice state before iteration
For-Range (Map):
Go: for k, v := range m { }
TS: for (const [k, v] of m.entries()) { }
For-Range (String):
Go: for i, r := range str { }
TS: for (const [i, r] of $.rangeString(str)) { }
// Iterates over runes, not bytes
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DEFER TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Sync Defer:
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β func process() { β β function process() { β
β f := open("file") β β const $defer = new $.DisposableStack()
β defer f.Close() β ββββΆ β try { β
β // work with f β β let f = open("file") β
β } β β $defer.defer(() => f.Close()) β
β β β // work with f β
β β β } finally { β
β β β $defer.dispose() β
β β β } β
β β β } β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
Async Defer:
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β func process() { β β async function process() { β
β ch := make(chan int) β β await using $defer = β
β defer close(ch) β ββββΆ β new $.AsyncDisposableStack() β
β // use channel β β let ch = $.makeChannel(...) β
β } β β $defer.defer(() => ch.close()) β
β β β // use channel β
β β β } β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
goscript/
βββ cmd/goscript/ # CLI entry point
βββ compiler/ # Core v2 compiler owners
β βββ compiler.go # Public Go adapter over CompileService
β βββ service.go # Pipeline coordinator
β βββ compile-request.go # Adapter normalization and request validation
β βββ package-graph.go # go/packages loading and package graph facts
β βββ semantic-model.go # Semantic/package/type/method/async facts
β βββ lowering.go # Go AST + semantic facts to compiler IR
β βββ lowered-model.go # Lowered IR structs
β βββ typescript-emitter.go # Deterministic TypeScript rendering
β βββ runtime-contract.go # Generated helper/import contract
β βββ override-registry.go # Handwritten gs/ package metadata/copy plans
β βββ wasm_api.go # Browser source-compilation adapter
βββ gs/ # Runtime & handwritten packages
β βββ builtin/ # @goscript/builtin runtime
β β βββ index.ts # Main exports
β β βββ varRef.ts # VarRef type
β β βββ slice.ts # Slice helpers
β β βββ channel.ts # Channel implementation
β β βββ map.ts # Map helpers
β β βββ type.ts # Runtime type info
β β βββ defer.ts # Defer support
β β βββ errors.ts # Error handling
β βββ [std packages]/ # Handwritten std library
βββ design/ # Design documentation
β βββ DESIGN.md # Main design doc
β βββ ASYNC.md # Async design
β βββ VAR_REFS.md # VarRef design
β βββ ...
βββ tests/ # Compliance test suite
β βββ tests/ # 260+ test cases
βββ docs/
βββ explainer.md # This file
GoScript achieves Go-to-TypeScript translation through:
- Owner-Separated Pipeline: Request, graph, semantic, lowering, runtime, override, and emit owners keep durable rules in one place
- VarRef System: Enables pointer semantics in TypeScript
- Function Coloring: Automatically determines async/sync boundaries
- Runtime Helpers: Provide Go-like semantics for slices, channels, maps
- Value Semantics: Clone methods preserve Go's copy behavior
- Structured Diagnostics: Unsupported requests or syntax fail before output is written
- Comprehensive Type Mapping: Go types become idiomatic TypeScript
The result is maintainable TypeScript that preserves Go's behavior while feeling natural to TypeScript developers.