gents
is a tool for generating Typescript interfaces from Rust code.
It bridges the gap between Rust backends and TypeScript frontends by automatically generating type definitions, making it easy to use serde-json
for communication between the two languages without manually maintaining duplicate type stubs.
Why use gents?
- Automatic type synchronization: When your Rust data models change, you can instantly regenerate matching TypeScript interfaces, ensuring your frontend and backend always stay in sync.
- Eliminate manual boilerplate: No need to hand-write or update TypeScript types every time your Rust structs or enums change, reducing human error and saving time.
- Ideal for rapid iteration: If your API or data structures evolve frequently,
gents
lets you iterate quickly without worrying about type mismatches. - Great for fullstack Rust+TS/JS projects: Whether you’re building a RESTful API, a WebAssembly app, or any project where Rust and TypeScript need to share data structures,
gents
makes cross-language type safety effortless.
Typical scenarios where gents
shines:
- Building a web server in Rust with a TypeScript (React/Vue/Svelte) frontend, and you want to keep your types always correct and up-to-date.
- Developing a WASM project where Rust and TypeScript interact closely.
- Working in a team where backend and frontend developers need a single source of truth for data models.
This tool is designed for LogiSheets and is inspired by ts-rs
. Many thanks to them!
Your issues and PRs are welcome!
In your Cargo.toml
:
[dev-dependencies]
gents = "1.0"
gents_derives = "1.0"
Use gents_derives::TS
and specify the output file:
use gents_derives::TS;
#[derive(TS)]
#[ts(file_name = "person.ts", rename_all = "camelCase")]
pub struct Person {
pub age: u16,
pub en_name: String,
}
#[derive(TS)]
#[ts(file_name = "group.ts", rename_all = "camelCase")]
pub struct Group {
pub name: String,
pub capacity: u16,
pub members: Vec<Person>,
pub leader: Option<Person>,
}
You can use rename_all
and rename
for field naming policies.
Write a binary or unit test:
#[ignore]
#[test]
fn gents() {
use gents::FileGroup;
let mut group = FileGroup::new();
// Add your root types; dependencies will be included automatically
group.add::<Group>();
// The second argument controls whether to generate index.ts
group.gen_files("outdir", false);
}
Since Group
has dependencies on Person
, gents
will automatically include Person
in the generated files.
- Run with
cargo test -- --ignored
to generate files.
- All generated
.ts
files will be placed in the directory you specify (e.g.,outdir
). - If you set the second argument of
gen_files
totrue
, anindex.ts
exporting all types will be generated.
- Add the generated
.ts
files to your frontend project (or link via a monorepo). - Now your TypeScript code can safely import and use the types generated from Rust, ensuring type consistency.
- Dependencies auto-detection:
When you add a type to
FileGroup
, all of its dependencies (other structs/enums it uses) are automatically included. - Customizing output: You can control file names, field naming, and more via attributes.
- Use in CI: You can run the generation test in your CI pipeline to ensure TypeScript types are always up to date.
-
ts-rs
generates the files when runningcargo test
and in this way we must commit those generated files into our repo. It is not necessary and is even an obstacle when we use other build tools likebazel
.gents
acts as a binary to generate Typescript files. -
gents
introduces a concept Group that from all the members in this group files generated will be placed in the same directory. Group is seperate from the other group even though they can share some dependecies. Therefore,gents
requires you to specify the file_name on structs or enums and to specify the dir on group, whilets-rs
requires specifing the path on every item. -
gents
helps you manage the export files. And it gathers all the dependencies automatically. -
gents
is well support for referencing other crates. -
Code generated by
ts-rs
is not match our coding style.