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,
gentslets 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,
gentsmakes 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 -- --ignoredto generate files.
- All generated
.tsfiles will be placed in the directory you specify (e.g.,outdir). - If you set the second argument of
gen_filestotrue, anindex.tsexporting all types will be generated.
- Add the generated
.tsfiles 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-rsgenerates the files when runningcargo testand 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.gentsacts as a binary to generate Typescript files. -
gentsintroduces 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,gentsrequires you to specify the file_name on structs or enums and to specify the dir on group, whilets-rsrequires specifing the path on every item. -
gentshelps you manage the export files. And it gathers all the dependencies automatically. -
gentsis well support for referencing other crates. -
Code generated by
ts-rsis not match our coding style.