tauri-ts-generator

Crates.iotauri-ts-generator
lib.rstauri-ts-generator
version1.8.1
created_at2025-12-27 21:31:48.823054+00
updated_at2026-01-09 11:52:00.836617+00
descriptionCLI tool to generate TypeScript bindings from Tauri commands
homepage
repositoryhttps://github.com/Dudude-bit/tauri-codegen
max_upload_size
id2007864
size290,921
(Dudude-bit)

documentation

README

tauri-ts-generator

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.

Features

  • Automated Scanning: Recursively scans your src-tauri directory for commands and types.
  • Type Safety: Generates exact TypeScript definitions for Rust structs, enums, and type aliases.
  • Serde Support:
    • Respects #[serde(rename = "...")] attributes, preserving the exact name and overriding camelCase conversion.
    • Handles #[serde(rename_all = "...")] for enums and structs.
    • Supports #[serde(tag = "...")], #[serde(content = "...")], and #[serde(untagged)] enum representations.
    • Supports #[serde(flatten)] to generate TypeScript intersection types.
    • Fields with #[serde(skip)] are excluded from TypeScript output.
    • Support for #[ts(optional)] attribute on Option fields to generate prop?: T instead of T | null.
    • Provides #[derive(tauri_ts_generator::TS)] to register the ts attribute namespace.
  • Smart Type Mapping:
    • Maps common Rust types (String, Vec, Option, Result) to TypeScript equivalents.
    • Handles external crate types like chrono::DateTime, uuid::Uuid, url::Url, and rust_decimal::Decimal.
  • Async Handling: Correctly generates Promise<T> for async commands.
  • Tauri Integration:
    • Automatically imports invoke from @tauri-apps/api/core.
    • Supports #[tauri::command(rename_all = "...")] to control argument casing (e.g. snake_case).
  • Macro Support: Optional integration with cargo-expand to resolve types generated by macros (e.g., progenitor).
  • Conflict Resolution: Detects and handles naming conflicts or ambiguous imports.

Installation

cargo install tauri-ts-generator

Or run directly from source:

cargo run --release -- generate

Quick Start

  1. Initialize Configuration (Run in your Tauri project root):

    tauri-ts-generator init
    

    This creates a tauri-codegen.toml file.

  2. Generate Bindings:

    tauri-ts-generator generate
    

Configuration (tauri-codegen.toml)

Customize the generator behavior using the TOML configuration file.

[input] Section

Defines 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] Section

Defines 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] Section

Customize 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. ""

Type Mappings

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

Supported External Types

Common types from popular crates are mapped automatically:

  • Chrono: DateTime, NaiveDate, NaiveTimestring
  • Time: OffsetDateTime, Datestring
  • Uuid: Uuidstring
  • Url: Urlstring
  • Rust Decimal: Decimalstring
  • Std: Path, PathBuf, IpAddrstring; Durationnumber

Examples

1. Basic Command & Struct

Rust:

#[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 });
}

2. Serde Rename (Exact Casing)

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
}

3. Enums

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, UPPERCASE
  • camelCase, PascalCase
  • snake_case, SCREAMING_SNAKE_CASE
  • kebab-case, SCREAMING-KEBAB-CASE

4. Command Arguments Rename

Use 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 });
}

5. Option with Undefined

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)]
}

6. Skipping Fields

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)]
}

7. Serde Flatten (Intersection Types)

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).

CLI Reference

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)

License

MIT

Commit count: 36

cargo fmt