typewire
Work in progress -- not production ready. API and schema format may change.
Derive-based cross-language type bridging for Rust.
#[derive(Typewire)] generates bidirectional conversion methods and compile-time
schema records from your Rust types. Define types once in Rust, get type-safe
foreign-language bindings and declarations automatically.
Currently supported targets:
- WebAssembly (wasm32) -- generates
to_js,from_js,patch_jsviawasm-bindgen, with TypeScript.d.tsgeneration - Kotlin and Swift -- planned
Relationship with wasm-bindgen
Typewire does not replace wasm-bindgen -- it builds on top of it. Where
wasm-bindgen handles the low-level ABI boundary (function exports, memory
management, JS glue code), typewire adds support for richer type shapes that
wasm-bindgen alone cannot express: enums with data, tagged unions, nested
structs, optional fields, generic types, HashMap/BTreeMap, and more. The
generated code uses wasm-bindgen's JsValue and js-sys primitives under
the hood.
Quick look
use Typewire;
Build to wasm, run the CLI, and get TypeScript declarations:
export type Command =
| { type: "Add"; data: Todo }
| { type: "Toggle"; data: { id: number } }
| { type: "Remove"; data: { id: number } }
| { type: "SetPriority"; data: { id: number; priority: Priority } };
export type Priority = "low" | "medium" | "high";
export interface Todo {
id: number;
title: string;
completed: boolean;
description: string | null;
priority: Priority;
tags: string[];
}
How it works
#[derive(Typewire)] --> encode (link section) --> decode --> TypeScript .d.ts
(derive) (typewire-schema) (CLI) (codegen)
-
Derive --
#[derive(Typewire)]analyzes your types and generates target-specific conversion methods. With theschemasfeature enabled, it also embeds schema records in a binary link section. -
Extract -- The
typewireCLI reads schema records from compiled binaries and generates typed declarations for the target language. -
Strip -- The CLI strips the schema section from the binary so it doesn't ship to production.
Features
| Feature | What it enables |
|---|---|
derive (default) |
Re-exports #[derive(Typewire)] |
schemas |
Embeds schema records in link sections for codegen |
cli |
Binary target for schema extraction and declaration generation |
uuid |
Typewire impl for uuid::Uuid |
chrono |
Typewire impl for chrono::DateTime, NaiveDate, etc. |
url |
Typewire impl for url::Url |
indexmap |
Typewire impl for IndexMap and IndexSet |
bytes |
Typewire impl for bytes::Bytes |
base64 |
Base64 encoding for Vec<u8> fields via #[typewire(base64)] |
serde_json |
Typewire impl for serde_json::Value |
Serde compatibility
#[typewire(...)] supports the same attributes as serde:
- Container:
rename_all,tag,content,untagged,transparent,default,deny_unknown_fields,from,try_from,into - Variant:
rename,alias,skip,other,untagged - Field:
rename,alias,skip,default,flatten,skip_serializing_if,with = "serde_bytes",base64,display,lenient
When a type also derives Serialize/Deserialize, typewire reads #[serde(...)]
attributes too, so you don't need to duplicate them. But when only deriving
Typewire, prefer #[typewire(...)].
Efficient patching
patch_js performs structural diffing -- it only touches the JS properties that
actually changed. For collections, it uses LCS-based diffing to emit minimal
splice operations instead of replacing the entire array.
CLI usage
# Install
# Generate TypeScript from a wasm binary
# The schema section is stripped automatically (use --no-strip to keep it)
Workspace
| Crate | Role |
|---|---|
typewire |
Main library: trait, primitive/compound impls, CLI binary |
typewire-derive |
Proc-macro: #[derive(Typewire)] |
typewire-schema |
Schema metadata: binary format, encode/decode, emitters |
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.