ts-rs

Generate TypeScript type bindings directly from your Rust types via a derive macro

Library
Cargo
v12.0.1
1,861stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
60/100Good
Development Activity56
Maintenance28
Community56
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture78
Code Quality80
Innovation78
Learning Curve82

ts-rs is a Rust crate that generates matching TypeScript type definitions straight from your Rust structs and enums using a #[derive(TS)] procedural macro, so a Rust backend and a TypeScript frontend can share one source of truth for their data shapes instead of maintaining hand-written, drift-prone .d.ts files. It understands serde attributes (rename, rename_all, skip, tag, flatten, etc.) out of the box, so types already annotated for JSON serialization typically need no extra work to also export correct TypeScript.

Beyond the basic derive, ts-rs supports generics, tuple structs, enums (including serde’s internally/externally/adjacently tagged and untagged representations), and optional integrations for common external types (chrono, uuid, bigdecimal, bytes, url, indexmap, semver, tokio, jiff, and more) behind Cargo feature flags, plus a format feature that runs generated output through a TypeScript formatter.

What You Get

  • A #[derive(TS)] procedural macro that generates a matching TypeScript interface/type for any annotated Rust struct or enum
  • Automatic recognition of serde attributes (rename, rename_all, skip, tag, flatten, tagged/untagged enum representations) so serialization and TypeScript-export stay consistent
  • Feature-gated bindings for common external crates — chrono, uuid, bigdecimal, bytes, url, indexmap, ordered-float, semver, tokio, jiff, arrayvec, smol_str, heapless, bson — so third-party types export sensible TypeScript equivalents
  • An export() mechanism (and export_to) for writing generated .ts files to disk, plus an optional format feature that runs output through dprint-plugin-typescript for consistent formatting
  • Support for generics, tuple structs, and all of serde’s enum representations (internally/externally/adjacently tagged, untagged)

Common Use Cases

  • Keeping a Rust backend’s API request/response types in sync with a TypeScript frontend without maintaining hand-written .d.ts files
  • Generating TypeScript types for a Tauri or WASM-frontend application built on shared Rust domain types
  • Exporting TypeScript types for a Rust microservice’s message/event payloads consumed by a Node.js or browser client
  • Enforcing compile-time type safety across a full-stack Rust+TypeScript project’s serialization boundary

Under The Hood

Architecture — The repository is a Cargo workspace of three crates: macros (the #[derive(TS)] proc-macro implementation, parsing struct/enum ASTs and serde attributes to generate a TS trait implementation), ts-rs (the runtime crate defining the TS trait, the export/export_to machinery under src/export/, and per-external-type binding modules like chrono.rs, tokio.rs, serde_json.rs, jiff.rs), and example (a workspace member demonstrating usage patterns), with e2e housing end-to-end conformance tests that assert generated TypeScript matches expectations.

Tech Stack — Pure Rust, built on syn/quote/proc-macro2 for the derive macro (standard Rust proc-macro tooling), with an extensive matrix of Cargo.toml feature flags (chrono-impl, uuid-impl, tokio-impl, jiff-impl, etc.) gating optional dependencies so consumers only compile bindings for external types they actually use. The optional format feature depends on dprint-plugin-typescript for output formatting.

Code Quality — The e2e directory of end-to-end tests checking exact generated-TypeScript output is a strong quality signal for a code-generation crate, where subtle formatting or type-mapping regressions are otherwise easy to miss in unit tests alone. A maintained CHANGELOG.md and CONTRIBUTING.md indicate an established contribution process; the per-external-crate integration modules (one file per supported type, e.g. chrono.rs, jiff.rs) keep each integration’s mapping logic isolated and independently testable.

API Design — The core workflow is a single #[derive(TS)] attribute reusing serde’s existing attribute vocabulary, so teams that already annotate their types for serde::Serialize/Deserialize get TypeScript export with near-zero additional annotation burden — a deliberate design choice that minimizes the learning curve for the crate’s primary audience (Rust teams already using serde for their API layer).

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search