Important: Use Australian english spelling and grammar conventions.
Important: Build everything with npm run build
Typical is a TypeScript transformer that adds runtime validation to TypeScript code automatically. It uses a Go-based compiler that leverages the TypeScript type checker to generate validation code at build time.
No changes to your code required - just use TypeScript types and Typical handles the rest.
typical/
├── src/ # TypeScript wrapper/client
│ ├── transformer.ts # Thin wrapper around Go compiler
│ ├── config.ts # Configuration handling
│ ├── cli.ts # CLI tool
│ ├── esm-loader.ts # ESM loader for Node.js
│ └── timing.ts # Build timing
├── packages/
│ ├── compiler/ # Go compiler package
│ │ ├── go/ # Go source code
│ │ │ ├── cmd/typical/ # Main binary
│ │ │ └── internal/
│ │ │ ├── server/ # Binary protocol server
│ │ │ ├── transform/ # AST transformation
│ │ │ └── codegen/ # Validator code generation
│ │ ├── src/ # TypeScript client
│ │ │ ├── client.ts # Communicates with Go binary
│ │ │ └── protocol.ts # MessagePack-like binary protocol
│ │ └── bin/ # Compiled Go binary
│ ├── unplugin/ # Vite/Webpack/Rollup plugin
│ └── bun-plugin/ # Bun plugin
├── samples/ # Example projects
│ ├── esm/ # ESM loader example
│ ├── ttsx/ # ttsx wrapper example
│ ├── bun/ # Bun plugin example
│ └── vite-react/ # Vite + React example
└── bench/ # Benchmarks
The core transformation logic is in Go:
- internal/server/ - Binary protocol server that receives requests from Node.js
- internal/transform/ - AST visitor that finds functions, returns, casts to transform
- internal/codegen/ - Generates validator functions for TypeScript types
- client.ts - Spawns Go binary, sends/receives binary protocol messages
- protocol.ts - MessagePack-like encoding/decoding
- transformer.ts -
TypicalTransformerclass wraps the Go compiler client - esm-loader.ts - Node.js ESM loader hooks
# Build everything
pnpm run build
# Just build the compiler/go binary
cd packages/compiler/go && npm run build
# Run all samples
npm run samples
# Individual samples
npm run sample:esm
npm run sample:bun
npm run sample:vite-react
npm run sample:ttsx- Node.js calls
TypicalTransformer.transform(fileName) - Go binary receives request via binary protocol
- Go loads TypeScript project using
typescript-goshim - Go walks AST, finds functions/returns/casts to validate
- Go generates inline validator functions for each type
- Go returns transformed TypeScript code
- Node.js (or bundler) transpiles the TypeScript to JavaScript
// Input
function greet(user: User): string {
return `Hello ${user.name}`;
}
// Output (conceptual)
function greet(user: User): string {
((v, n) => { /* validate User */ })(user, "user");
return ((v, n) => { /* validate string */ })(`Hello ${user.name}`, "return value");
}The Go binary communicates via stdin/stdout using a MessagePack-like binary protocol:
- Request:
[MessageType, RequestId, Payload] - Response:
[MessageType, RequestId, Payload]
Request IDs use format method:counter (e.g., transformFile:0) to correlate concurrent requests.
- TypeScript: ES modules, semicolons, strict mode
- Go: Standard Go formatting (
go fmt) - Use pnpm for package management
- Add to
TypicalConfiginterface insrc/config.ts - Add to
Configstruct inpackages/compiler/go/internal/transform/config.go - Use the option in the Go transform/codegen code
- Update README.md
# Verbose logging from both Go and TypeScript
DEBUG=1 npm run sample:esm
# See Go compiler output
DEBUG=1 node --import @elliots/typical/esm script.ts- Generic type parameters (
T) can't be validated at runtime - DOM types (Window, Document, Element, etc.) are skipped
- Class types validate instanceof only, not properties