| Crates.io | tauri-ts-generator |
| lib.rs | tauri-ts-generator |
| version | 1.8.1 |
| created_at | 2025-12-27 21:31:48.823054+00 |
| updated_at | 2026-01-09 11:52:00.836617+00 |
| description | CLI tool to generate TypeScript bindings from Tauri commands |
| homepage | |
| repository | https://github.com/Dudude-bit/tauri-codegen |
| max_upload_size | |
| id | 2007864 |
| size | 290,921 |
A powerful CLI tool to automatically generate TypeScript bindings from your Rust Tauri commands, structs, and enums.
tauri-ts-generator scans your Rust source code, parses #[tauri::command] macros and data structures, and generates type-safe TypeScript interfaces and invocation functions. This eliminates manual typing boilerplate and ensures your frontend and backend are always in sync.
src-tauri directory for commands and types.#[serde(rename = "...")] attributes, preserving the exact name and overriding camelCase conversion.#[serde(rename_all = "...")] for enums and structs.#[serde(tag = "...")], #[serde(content = "...")], and #[serde(untagged)] enum representations.#[serde(flatten)] to generate TypeScript intersection types.#[serde(skip)] are excluded from TypeScript output.#[ts(optional)] attribute on Option fields to generate prop?: T instead of T | null.#[derive(tauri_ts_generator::TS)] to register the ts attribute namespace.String, Vec, Option, Result) to TypeScript equivalents.chrono::DateTime, uuid::Uuid, url::Url, and rust_decimal::Decimal.Promise<T> for async commands.invoke from @tauri-apps/api/core.#[tauri::command(rename_all = "...")] to control argument casing (e.g. snake_case).cargo-expand to resolve types generated by macros (e.g., progenitor).cargo install tauri-ts-generator
Or run directly from source:
cargo run --release -- generate
Initialize Configuration (Run in your Tauri project root):
tauri-ts-generator init
This creates a tauri-codegen.toml file.
Generate Bindings:
tauri-ts-generator generate
tauri-codegen.toml)Customize the generator behavior using the TOML configuration file.
[input] SectionDefines where the generator looks for code.
| Key | Description | Default |
|---|---|---|
source_dir |
Root directory of your Rust source code. | "src-tauri/src" |
exclude |
List of directories or files to ignore. | ["tests", "target"] |
use_cargo_expand |
Enable if you use macro-generated types (requires cargo-expand). |
false |
cargo_manifest |
Path to Cargo.toml for cargo-expand. Auto-detected if empty. |
None |
[output] SectionDefines where the generated TypeScript files are saved.
| Key | Description | Default |
|---|---|---|
types_file |
Path for generated interfaces/types. | "src/generated/types.ts" |
commands_file |
Path for generated invoke functions. | "src/generated/commands.ts" |
[naming] SectionCustomize naming conventions for generated types and functions.
| Key | Description | Default |
|---|---|---|
type_prefix |
Prefix added to all generated interface names (e.g., "I"). | "" |
type_suffix |
Suffix added to all generated interface names (e.g., "DTO"). | "" |
function_prefix |
Prefix for generated command functions. | "" |
function_suffix |
Suffix for generated command functions. | "" |
The generator maps Rust types to TypeScript as follows:
| Rust Type | TypeScript Type |
|---|---|
String, &str, char |
string |
i8...i64, u8...u64, f32, f64 |
number |
bool |
boolean |
Option<T> |
T | null (default), or optional field ?: T (with #[ts(optional)]) |
Vec<T> |
T[] |
HashMap<K, V> |
Record<K, V> (if K is string/number) |
Result<T, E> |
Promise<T> (in return types) |
() / Unit |
void |
bytes::Bytes |
number[] |
serde_json::Value |
unknown |
Common types from popular crates are mapped automatically:
DateTime, NaiveDate, NaiveTime → stringOffsetDateTime, Date → stringUuid → stringUrl → stringDecimal → stringPath, PathBuf, IpAddr → string; Duration → numberRust:
#[derive(Serialize)]
pub struct User {
pub id: i32,
pub name: String,
}
#[tauri::command]
pub async fn get_user(id: i32) -> Result<User, String> { /* ... */ }
TypeScript Output:
export interface User {
id: number;
name: string;
}
export async function getUser(id: number): Promise<User> {
return invoke<User>("get_user", { id });
}
When #[serde(rename = "...")] is used, the generator preserves the exact casing, skipping the default camelCase conversion.
Rust:
#[derive(Serialize)]
pub struct Config {
#[serde(rename = "API_KEY")]
pub api_key: String,
pub retries: i32,
}
TypeScript Output:
export interface Config {
API_KEY: string; // Exactly as renamed in Rust
retries: number; // Default camelCase
}
Supports various serde representations.
Rust:
#[derive(Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Status {
Active,
Inactive,
}
TypeScript Output:
export type Status = "ACTIVE" | "INACTIVE";
Supported rename_all values:
lowercase, UPPERCASEcamelCase, PascalCasesnake_case, SCREAMING_SNAKE_CASEkebab-case, SCREAMING-KEBAB-CASEUse rename_all on commands to control argument keys in the invoke payload.
Rust:
#[tauri::command(rename_all = "snake_case")]
pub fn update_user(user_id: i32, new_email: String) { /* ... */ }
TypeScript Output:
export async function updateUser(userId: number, newEmail: string): Promise<void> {
// Arguments are mapped to snake_case in the payload
return invoke<void>("update_user", { user_id: userId, new_email: newEmail });
}
By default, Option<T> maps to T | null. You can use the #[ts(optional)] attribute to map it to prop?: T instead.
Note: You must add
#[derive(tauri_ts_generator::TS)]to enable the#[ts(...)]attribute on your structs.
Rust:
use tauri_ts_generator::TS;
#[derive(Serialize, TS)]
pub struct Config {
pub name: Option<String>,
#[ts(optional)]
pub volume: Option<f32>,
}
TypeScript Output:
export interface Config {
name: string | null; // Default behavior
volume?: number; // With #[ts(optional)]
}
Fields with #[serde(skip)] are excluded from the TypeScript output. Note that skip_serializing and skip_deserializing are not excluded, as they only affect one direction of serialization.
Rust:
#[derive(Serialize)]
pub struct User {
pub id: i32,
pub name: String,
#[serde(skip)]
pub internal_cache: Vec<u8>, // Excluded from TypeScript
#[serde(skip_serializing)]
pub password_hash: String, // Kept in TypeScript (needed for input)
}
TypeScript Output:
export interface User {
id: number;
name: string;
passwordHash: string; // skip_serializing fields are kept
// internal_cache is excluded due to #[serde(skip)]
}
Use #[serde(flatten)] to embed one struct's fields into another. The generator produces TypeScript intersection types.
Rust:
#[derive(Serialize)]
pub struct Address {
pub city: String,
pub country: String,
}
#[derive(Serialize)]
pub struct User {
pub name: String,
#[serde(flatten)]
pub address: Address,
}
TypeScript Output:
export interface Address {
city: string;
country: string;
}
export type User = {
name: string;
} & Address;
This works correctly for both command arguments (input) and return types (output).
tauri-ts-generator <COMMAND> [OPTIONS]
Commands:
generate Generate TypeScript bindings
init Create a default configuration file
help Print help information
Options:
-v, --verbose Enable verbose logging (useful for debugging scanning/parsing)
-c, --config Path to config file (default: tauri-codegen.toml)
MIT