🖊️ typewriter
Cross-Language Type Synchronization SDK for Rust Define your types once in Rust. Get perfectly matching types in TypeScript, Python, Go, Swift, Kotlin, GraphQL, JSON Schema, Ruby, PHP, and Dart — automatically, forever.
| 🦀 Rust Core | 🌐 Polyglot | ⚡ Zero Drift | 🔧 Proc Macro |
|---|---|---|---|
| proc macro powered | 10+ languages | forever in sync | zero boilerplate |
⚠️ The Problem
Every full-stack team using Rust on the backend faces the same invisible tax: keeping types in sync across languages.
Backend dev changes: Frontend/Mobile dev sees:
───────────────────── ─────────────────────────
pub struct User { interface User {
pub id: Uuid, ──────► id: string;
pub email: String, email: string;
pub name: String, ← ADDED // ❌ missing: name
pub role: Role, role: Role;
} }
// Runtime: undefined is not a string
OpenAPI codegen? Only works for HTTP APIs. Protobuf? Heavy toolchain. Manual sync? Humans forget.
Root cause: Every existing solution uses an intermediary format as source of truth instead of the Rust source itself.
✅ The Solution
typewriter makes your Rust structs and enums the single, permanent source of truth for all your type definitions.
Annotate once → generate everywhere. When the Rust type changes, every generated file updates automatically. No hand-maintained schema file. No intermediary format. No drift. Ever.
use TypeWriter;
use ;
// On cargo build, auto-generates:
// ✅ ./generated/typescript/user-profile.ts
// ✅ ./generated/typescript/user-profile.schema.ts
// ✅ ./generated/python/user_profile.py
// ✅ ./generated/go/user_profile.go
// ✅ ./generated/graphql/user_profile.graphql
// ✅ ./generated/json-schema/user_profile.schema.json
🚀 Quick Start
1. Add to Cargo.toml
[]
= "0.5.2"
= { = "1", = ["derive"] }
2. Annotate your types
use TypeWriter;
use ;
/// A user in the system.
3. Build
That's it. Check ./generated/typescript/, ./generated/python/, and ./generated/go/ for your generated files.
📦 Generated Output
TypeScript → user.ts
// Auto-generated by typewriter v0.4.2. DO NOT EDIT.
/**
* A user in the system.
*/
export interface User {
id: string;
email: string;
name: string;
age?: number | undefined;
is_active: boolean;
tags: string[];
}
TypeScript Zod Schema → user.schema.ts
import { z } from 'zod';
export const UserSchema = z.object({
"id": z.string(),
"email": z.string(),
"name": z.string(),
"age": z.number().optional(),
"is_active": z.boolean(),
"tags": z.array(z.string()),
});
To consume generated schemas at runtime, install zod in your TS app. Set [typescript].zod = false to disable global schema output, and use #[tw(zod)]/#[tw(zod = false)] for per-type overrides:
Python → user.py
# Auto-generated by typewriter v0.4.2. DO NOT EDIT.
"""A user in the system."""
:
:
:
: = None
:
:
Go → user.go
// Code generated by typewriter v0.4.2. DO NOT EDIT.
// Source: User
package types
// A user in the system.
type User struct
🎯 Features
✅ Core Generation Platform (v0.4.2)
- TypeScript emitter —
export interface,export typeunions, optional fields - TypeScript Zod schemas — sibling
<type>.schema.tsfiles withexport const <Type>Schema = ...(enabled by default, configurable via[typescript].zodand#[tw(zod)]; runtimezoddependency) - Python emitter — Pydantic v2
BaseModel,Enum,UnionwithLiteraldiscriminators - Go emitter — native
structparsing,interfacedata-carrying enums, andomitemptyoptional pointers - Swift emitter —
Codablestructs and enums withCodingKeys - Kotlin emitter —
data classandsealed classwithkotlinx.serialization - GraphQL SDL emitter —
type,enum,uniondefinitions with custom scalars (DateTime,JSON) - JSON Schema emitter — Draft 2020-12
objectschemas,stringenums,oneOfcomposition with format annotations (uuid,date-time,date) - Generic types —
Pagination<T>→export interface Pagination<T>(TS) /class Pagination(BaseModel, Generic[T])(Python) - Cross-file imports — auto
import type { X } from './file'(TS) /from .file import X(Python) - Serde compatibility — auto-reads
#[serde(rename, skip, tag, flatten)] - Custom attributes —
#[tw(skip)],#[tw(rename)],#[tw(optional)],#[tw(zod)] - Doc comments — Rust
///flows to JSDoc in TS, docstrings in Python,"""in GraphQL - Smart type unwrapping —
Box<T>,Arc<T>,Rc<T>transparently unwrapped - Feature-gated emitters — compile only what you need
- TOML config —
typewriter.tomlfor output directories, file naming styles, readonly mode
✅ Phase 3 CLI (v0.3.1)
typewriter generate <file>andtypewriter generate --alltypewriter check --ciwith drift detection gatetypewriter check --json/--json-outstructured report outputtypewriter watch [path]auto-regeneration on Rust file savecargo typewriter ...subcommand support viacargo-typewritertypebridge-clipackage published as version0.2.2.
See CLI Guide for full command reference and JSON schema.
🔮 Coming Soon
- VSCode / Neovim extensions
- Plugin API for custom language backends
See the full Roadmap for details.
⚙️ Configuration
Create a typewriter.toml at your project root (optional — sensible defaults are used):
[]
= "../frontend/src/types"
= "kebab-case"
= false
= true
[]
= "../api/schemas"
= true
[]
= "../schema/types"
= "snake_case"
[]
= "../schemas"
= "snake_case"
See Configuration Guide for all options.
🏷️ Attributes
Serde Compatibility
typewriter automatically reads #[serde(...)] attributes — no need to repeat them:
Custom Attributes
See the full Attributes Guide.
📊 Type Mapping Reference
| Rust Type | TypeScript | Python | Go | Swift | Kotlin | GraphQL | JSON Schema |
|---|---|---|---|---|---|---|---|
String |
string |
str |
string |
String |
String |
String |
string |
u8–u32, i8–i32, f32, f64 |
number |
int / float |
uint* / int* / float* |
UInt* / Int* / Float/Double |
UInt / Int / Float/Double |
Int / Float |
integer / number |
u64, i64 |
bigint |
int |
uint64 / int64 |
UInt64 / Int64 |
ULong / Long |
String |
integer |
bool |
boolean |
bool |
bool |
Bool |
Boolean |
Boolean |
boolean |
Option<T> |
T | undefined |
Optional[T] |
*T with omitempty |
T? |
T? = null |
nullable (no !) |
not in required |
Vec<T> |
T[] |
list[T] |
[]T |
[T] |
List<T> |
[T!] |
array |
HashMap<K,V> |
Record<K, V> |
dict[K, V] |
map[K]V |
[K: V] |
Map<K, V> |
JSON |
object |
Uuid |
string |
UUID |
string |
UUID |
String |
ID |
string + uuid format |
DateTime<Utc> |
string |
datetime |
time.Time |
Date |
kotlinx.datetime.Instant |
DateTime |
string + date-time format |
See full type mapping reference for all supported types.
🏗️ Architecture
typewriter is a Cargo workspace with focused, independently publishable crates:
typewriter/
├── typewriter-core/ ← IR types, TypeMapper trait, config
├── typewriter-engine/ ← Shared scan/parse/emit + drift orchestration
├── typewriter-macros/ ← #[derive(TypeWriter)] proc macro
├── typewriter-typescript/ ← TypeScript emitter
├── typewriter-python/ ← Python emitter
├── typewriter-go/ ← Go emitter
├── typewriter-swift/ ← Swift emitter
├── typewriter-kotlin/ ← Kotlin emitter
├── typewriter-graphql/ ← GraphQL SDL emitter
├── typewriter-json-schema/ ← JSON Schema emitter
├── typewriter-cli/ ← `typebridge-cli` package (`typewriter` + `cargo-typewriter` binaries)
├── typewriter/ ← Main user-facing crate (re-exports)
├── typewriter-test/ ← Snapshot tests
└── example/ ← Working usage examples
See ARCHITECTURE.md for the full technical deep-dive.
🤝 Contributing
We welcome contributions! See CONTRIBUTING.md for:
- Development setup
- Code style guidelines
- How to add a new language emitter
- PR and review process
📄 License
Apache License 2.0
Copyright 2026 Aarambh Dev Hub
See LICENSE for the full text.
Built with ❤️ and 🦀 by Aarambh Dev Hub
Define once. Generate everywhere. Never drift again.
⭐ Star this repo if you find it useful!