ts-macro
ts-macro is a Rust procedural macro crate for generating TypeScript interface
bindings from Rust structs. It's designed for seamless Rust <-> TypeScript
interop in wasm-bindgen projects, making it easy to expose Rust types to
JavaScript and TypeScript consumers.

Overview
- Automatic TypeScript Interface Generation: Annotate your Rust struct with
#[ts]to generate a TypeScript interface with camelCase field names and accurate TypeScript types. - wasm-bindgen & js-sys Support: Designed for use with
wasm-bindgenand supports all JavaScript standard, built-in objects viajs-sys. - No Boilerplate: Field getter bindings and conversion methods are generated for you—no manual parsing required.
- Customizable Bindings: Use attributes to control TypeScript field names, types, and optionality.
- Nested Structs: Apply
#[ts]to each struct to support nested TypeScript interfaces. - Extensible Interfaces: Use the
extendsargument to extend TypeScript interfaces.
Example
This generates the following TypeScript interface:
interface IToken {
symbol: string;
/**
* @default 18
*/
decimals?: number | undefined;
totalSupply: bigint;
}
Nested Structs
To nest structs, apply the ts attribute to each struct individually. Then, the
bindings can be used as fields in other structs:
Arguments
The ts attribute accepts the following arguments when applied to a struct:
name: The name of the TypeScript interface and binding. Defaults toI{{StructName}}.extends: A comma-separated list of interfaces to extend.
This will generate the following TypeScript interfaces:
interface JsToken {
// ...
}
interface JsShareToken extends JsToken {
// ...
}
Field Arguments
To customize the TypeScript interface, the ts attribute can be applied to
individual fields of the struct. The attribute accepts the following arguments:
name: The name of the field in the TypeScript interface as a string. Defaults to the camelCase version of the Rust field name.type: The TypeScript type of the field as a string. Defaults to best-effort inferred.optional: Whether the field is optional in TypeScript. Defaults to inferred.
This generates:
interface IParams {
specialCASING: string;
specialFormat: `0x${string}`;
optionalFieldAndValue?: string | undefined;
optionalValue: string | undefined;
optionalField?: string;
}
Similar Projects
ts-rs– Generates TypeScript definition files from Rust structs and enums, but isn’t integrated with wasm-bindgen and doesn’t include bindings to parse the type from a JS value.tauri-specta– Similar idea for Tauri app backends.tsify– Same idea, but is built onserde-wasm-bindgenwhich doesn’t parse JS types, so APIs are limited to numbers and strings.
License
This project is licensed under the Apache 2.0.