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 |
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.
typewriter makes your Rust structs and enums the single, permanent source of truth for generated type definitions. The released CLI is installed and invoked as typebridge.
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 typebridge::TypeWriter;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, TypeWriter)]
#[sync_to(typescript, python)]
pub struct UserProfile {
pub id: Uuid,
pub email: String,
pub age: Option<u32>,
pub role: UserRole,
pub created_at: DateTime<Utc>,
}
// 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.jsonEnd users do not need Rust installed to use the released CLI.
macOS/Linux:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/aarambh-darshan/typewriter/releases/latest/download/typebridge-installer.sh | shWindows:
powershell -ExecutionPolicy Bypass -c "irm https://github.com/aarambh-darshan/typewriter/releases/latest/download/typebridge-installer.ps1 | iex"Rust users can also install from crates.io:
cargo install typebridge-cliThe primary binary is typebridge. The typewriter binary remains as a compatibility alias, and cargo typewriter remains available for Cargo-plugin workflows.
[dependencies]
typebridge = "1.0.0"
serde = { version = "1", features = ["derive"] }use typebridge::TypeWriter;
use serde::{Serialize, Deserialize};
/// A user in the system.
#[derive(Serialize, Deserialize, TypeWriter)]
#[sync_to(typescript, python, go)]
pub struct User {
pub id: String,
pub email: String,
pub name: String,
pub age: Option<u32>,
pub is_active: bool,
pub tags: Vec<String>,
}cargo buildOr use the CLI:
typebridge generate --all
typebridge check --ci
typebridge watchCheck ./generated/typescript/, ./generated/python/, and ./generated/go/ for your generated files.
// Auto-generated by typewriter v1.0.0. 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[];
}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:
npm install zod# Auto-generated by typewriter v1.0.0. DO NOT EDIT.
from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
"""A user in the system."""
id: str
email: str
name: str
age: Optional[int] = None
is_active: bool
tags: list[str]// Code generated by typewriter v1.0.0. DO NOT EDIT.
// Source: User
package types
// A user in the system.
type User struct {
Id string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Age *uint32 `json:"age,omitempty"`
Is_active bool `json:"is_active"`
Tags []string `json:"tags"`
}- 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
typebridge generate <file>andtypebridge generate --alltypebridge check --ciwith drift detection gatetypebridge watch [path]auto-regeneration on Rust file savetypebridge initto createtypewriter.tomltypebridge doctorfor project and installation diagnostics- Global
--config,--format text|json,--verbose, and--dry-run typebridge --versionand help output with CI-safe exit codescargo typewriter ...subcommand support viacargo-typewriter- Prebuilt release artifacts with shell and PowerShell installers, checksums, and GitHub Releases via cargo-dist
convert, JSON Schema input, OpenAPI input, and any-to-any conversion are not part of v1.0.0. The v1 CLI focuses on the current Rust-source generation workflow.
See CLI Guide for full command reference and JSON schema.
- VSCode / Neovim extensions
- Stable plugin ABI and plugin registry
See the full Roadmap for details.
Create a typewriter.toml at your project root (optional — sensible defaults are used):
[typescript]
output_dir = "../frontend/src/types"
file_style = "kebab-case"
readonly = false
zod = true
[python]
output_dir = "../api/schemas"
pydantic_v2 = true
[graphql]
output_dir = "../schema/types"
file_style = "snake_case"
[json_schema]
output_dir = "../schemas"
file_style = "snake_case"See Configuration Guide for all options.
typewriter automatically reads #[serde(...)] attributes — no need to repeat them:
#[derive(Serialize, Deserialize, TypeWriter)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PaymentStatus {
Pending,
Completed { transaction_id: String },
Failed { reason: String, code: u32 },
}pub struct User {
#[tw(skip)] // exclude from generated output
pub password_hash: String,
#[tw(rename = "displayName")] // override field name
pub username: String,
#[tw(optional)] // force optional even if not Option<T>
pub legacy_field: String,
}See the full Attributes Guide.
| 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.
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 (`typebridge`, `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.
We welcome contributions! See CONTRIBUTING.md for:
- Development setup
- Code style guidelines
- How to add a new language emitter
- PR and review process
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!