| Crates.io | ferro-type-gen |
| lib.rs | ferro-type-gen |
| version | 0.2.0 |
| created_at | 2026-01-11 20:54:25.337498+00 |
| updated_at | 2026-01-12 03:13:37.660734+00 |
| description | TypeScript file generation for ferro-type |
| homepage | |
| repository | https://github.com/iamnbutler/ferrotype |
| max_upload_size | |
| id | 2036423 |
| size | 42,255 |
Rust-to-TypeScript type generation via derive macro. Generates TypeScript type definitions from Rust structs and enums using an intermediate representation for deduplication and dependency ordering.
[dependencies]
ferro-type = "0.2.0"
#[derive(TS)] for structs and enumsTypeDef IR instead of string concatenationTypeRegistry for collecting types and rendering in dependency orderrename, rename_all, skip, flatten, etc.)use ferrotype::TS;
#[derive(TS)]
struct User {
id: String,
name: String,
age: i32,
}
// Renders as: { id: string; name: string; age: number }
use ferrotype::TS;
// Unit variants become string literal unions
#[derive(TS)]
enum Status {
Pending,
Active,
Completed,
}
// Renders as: "Pending" | "Active" | "Completed"
// Data variants become discriminated unions
#[derive(TS)]
enum Message {
Ping,
Text(String),
Error { code: i32, message: String },
}
// Renders as: { type: "Ping" } | { type: "Text"; value: string } | { type: "Error"; code: number; message: string }
use ferrotype::{TS, TypeRegistry};
#[derive(TS)]
struct User {
id: String,
name: String,
}
#[derive(TS)]
struct Post {
title: String,
author: User,
}
let mut registry = TypeRegistry::new();
registry.register::<Post>();
// Renders types in dependency order
let output = registry.render();
// type User = { id: string; name: string };
// type Post = { title: string; author: User };
| Attribute | Description |
|---|---|
#[ts(rename = "Name")] |
Rename the type |
#[ts(rename_all = "camelCase")] |
Rename all fields/variants |
#[ts(transparent)] |
Newtype becomes inner type directly |
#[ts(tag = "kind")] |
Custom discriminant field name (default: type) |
#[ts(content = "data")] |
Adjacent tagging with content field |
#[ts(untagged)] |
Plain union without discriminant |
| Attribute | Description |
|---|---|
#[ts(rename = "name")] |
Rename this field |
#[ts(skip)] |
Omit field from output |
#[ts(flatten)] |
Inline nested object fields |
#[ts(type = "Date")] |
Override TypeScript type |
#[ts(default)] |
Mark field as optional (?) |
#[ts(inline)] |
Inline type definition instead of reference |
#[ts(pattern = "${A}::${B}")] |
Template literal type |
#[ts(index = "T", key = "k")] |
Indexed access type (T["k"]) |
Extend existing TypeScript types:
#[derive(TS)]
#[ts(extends = "BaseEntity")]
struct User {
name: String,
email: String,
}
// Renders as: type User = BaseEntity & { name: string; email: string }
Generate branded ID types:
#[derive(TS)]
struct Order {
#[ts(pattern = "order-${string}")]
id: String,
}
// Renders as: id: `order-${string}`
Reference nested type properties:
#[derive(TS)]
struct Comment {
#[ts(index = "User", key = "id")]
author_id: String,
}
// Renders as: author_id: User["id"]
Supported values for rename_all: camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, SCREAMING-KEBAB-CASE
| Rust | TypeScript |
|---|---|
String, &str, char |
string |
i8..i64, u8..u64, f32, f64 |
number |
i128, u128 |
bigint |
bool |
boolean |
() |
void |
Option<T> |
T | null |
Vec<T> |
T[] |
HashMap<K, V> |
Record<K, V> |
Result<T, E> |
{ ok: true; value: T } | { ok: false; error: E } |
(A, B, ...) |
[A, B, ...] |
MIT