Rust的TypeScript类型定义生成工具typescript-type-def的使用,自动生成Rust到TypeScript的类型转换接口

Rust的TypeScript类型定义生成工具typescript-type-def的使用,自动生成Rust到TypeScript的类型转换接口

typescript-type-def是一个用于为Rust类型生成TypeScript类型定义的工具库。它可以帮助你生成一个包含类型定义的TypeScript模块,这些类型定义描述了Rust类型的JSON序列化格式。

主要用途

这个工具主要用于当你需要将Rust类型序列化为JSON(使用serde_json)并在TypeScript中使用时,可以自动生成对应的TypeScript类型定义,而不需要手动维护两套类型定义。

一个典型的使用场景是开发一个带有Rust后端和TypeScript前端的Web应用程序。如果前后端通信的数据在Rust中定义并使用serde_json进行编码/解码传输,你可以使用这个工具自动生成这些类型的TypeScript定义文件,以便在前端代码中安全使用。

特性

  • json_value:为serde_json中的JSON值类型添加TypeDef实现

示例

简单示例

use serde::Serialize;
use typescript_type_def::{
    write_definition_file,
    DefinitionFileOptions,
    TypeDef,
};

#[derive(Serialize, TypeDef)]
struct Foo {
    a: usize,
    b: String,
}

let ts_module = {
    let mut buf = Vec::new();
    let options = DefinitionFileOptions::default();
    write_definition_file::<_, Foo>(&mut buf, options).unwrap();
    String::from_utf8(buf).unwrap()
};
assert_eq!(
    ts_module,
    r#"// AUTO-GENERATED by typescript-type-def

export default types;
export namespace types{
export type Usize=number;
export type Foo={"a":types.Usize;"b":string;};
}
"#
);

let foo = Foo {
    a: 123,
    b: "hello".to_owned(),
};
let json = serde_json::to_string(&foo).unwrap();
// 这个JSON与上面的TypeScript类型定义匹配
assert_eq!(json, r#"{"a":123,"b":"hello"}"#);

大型代码库示例

当处理包含许多类型的大型代码库时,一个有用的模式是声明一个"API"类型别名,列出你想要为其生成定义的所有类型:

use serde::Serialize;
use typescript_type_def::{write_definition_file, TypeDef};

#[derive(Serialize, TypeDef)]
struct Foo {
    a: String,
}

#[derive(Serialize, TypeDef)]
struct Bar {
    a: String,
}

#[derive(Serialize, TypeDef)]
struct Baz {
    a: Qux,
}

#[derive(Serialize, TypeDef)]
struct Qux {
    a: String,
}

// 这个类型列出了我们想要为其生成定义的所有顶级类型
// 你不需要在这里列出API中的每种类型,只需要列出那些不会被其他类型引用的类型
// 注意Qux没有被提到,但仍然会被生成,因为它是Baz的依赖
type Api = (Foo, Bar, Baz);

let ts_module = {
    let mut buf = Vec::new();
    write_definition_file::<_, Api>(&mut buf, Default::default()).unwrap();
    String::from_utf8(buf).unwrap()
};
assert_eq!(
    ts_module,
    r#"// AUTO-GENERATED by typescript-type-def

export default types;
export namespace types{
export type Foo={"a":string;};
export type Bar={"a":string;};
export type Qux={"a":string;};
export type Baz={"a":types.Qux;};
}
"#
);

运行时创建类型信息示例

你也可以使用write_definition_file_from_type_infos和运行时创建的TypeInfo引用列表来创建定义文件:

use serde::Serialize;
use typescript_type_def::{write_definition_file_from_type_infos, TypeDef};

#[derive(Serialize, TypeDef)]
struct Foo {
    a: String,
}

#[derive(Serialize, TypeDef)]
struct Bar {
    a: String,
}

#[derive(Serialize, TypeDef)]
struct Baz {
    a: Qux,
}

#[derive(Serialize, TypeDef)]
struct Qux {
    a: String,
}

// 这个列表包含我们想要为其生成定义的所有顶级类型的类型信息
// 你不需要在这里列出API中的每种类型,只需要列出那些不会被其他类型引用的类型
// 注意Qux没有被提到,但仍然会被生成,因为它是Baz的依赖
let api = vec![
    &Foo::INFO,
    &Bar::INFO,
    &Baz::INFO,
];

let ts_module = {
    let mut buf = Vec::new();
    write_definition_file_from_type_infos(
        &mut buf,
        Default::default(),
        &api,
    )
    .unwrap();
    String::from_utf8(buf).unwrap()
};
assert_eq!(
    ts_module,
    r#"// AUTO-GENERATED by typescript-type-def

export default types;
export namespace types{
export type Foo={"a":string;};
export type Bar={"a":string;};
export type Qux={"a":string;};
export type Baz={"a":types.Qux;};
}
"#
);

完整示例Demo

下面是一个完整的示例,展示如何使用typescript-type-def生成TypeScript类型定义并在实际项目中使用:

// Cargo.toml
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// serde_json = "1.0"
// typescript-type-def = "0.5"

use serde::Serialize;
use typescript_type_def::{write_definition_file, TypeDef};

// 定义Rust数据结构
#[derive(Debug, Serialize, TypeDef)]
struct User {
    id: u64,
    username: String,
    email: String,
    is_active: bool,
    roles: Vec<Role>,
}

#[derive(Debug, Serialize, TypeDef)]
struct Role {
    name: String,
    permissions: Vec<String>,
}

// 生成TypeScript类型定义
fn generate_ts_definitions() -> String {
    let mut buf = Vec::new();
    write_definition_file::<_, User>(&mut buf, Default::default()).unwrap();
    String::from_utf8(buf).unwrap()
}

fn main() {
    // 生成TypeScript类型定义
    let ts_def = generate_ts_definitions();
    println!("Generated TypeScript definitions:\n{}", ts_def);
    
    // 创建一个User实例并序列化为JSON
    let user = User {
        id: 123,
        username: "john_doe".to_string(),
        email: "john@example.com".to_string(),
        is_active: true,
        roles: vec![
            Role {
                name: "admin".to_string(),
                permissions: vec!["read".to_string(), "write".to_string()],
            },
            Role {
                name: "user".to_string(),
                permissions: vec!["read".to_string()],
            },
        ],
    };
    
    let json = serde_json::to_string_pretty(&user).unwrap();
    println!("\nSerialized JSON:\n{}", json);
}

生成的TypeScript定义如下:

// AUTO-GENERATED by typescript-type-def

export default types;
export namespace types{
export type U64=number;
export type Role={"name":string;"permissions":string[];};
export type User={"id":types.U64;"username":string;"email":string;"is_active":boolean;"roles":types.Role[];};
}

对应的JSON输出:

{
  "id": 123,
  "username": "john_doe",
  "email": "john@example.com",
  "is_active": true,
  "roles": [
    {
      "name": "admin",
      "permissions": [
        "read",
        "write"
      ]
    },
    {
      "name": "user",
      "permissions": [
        "read"
      ]
    }
  ]
}

这个示例展示了如何:

  1. 定义Rust数据结构并使用TypeDef派生宏
  2. 自动生成对应的TypeScript类型定义
  3. 将Rust数据结构序列化为JSON
  4. 确保生成的JSON与TypeScript类型定义匹配

你可以将生成的TypeScript定义保存到前端项目的类型定义文件中,然后在TypeScript代码中使用这些类型,确保类型安全。


1 回复

Rust的TypeScript类型定义生成工具typescript-type-def使用指南

typescript-type-def是一个用于自动将Rust类型转换为TypeScript类型定义的工具,可以显著简化前后端类型同步的工作流程。

功能特点

  • 自动从Rust结构体和枚举生成TypeScript接口和类型
  • 支持大多数Rust标准类型到TypeScript的映射
  • 可配置的生成选项
  • 支持自定义类型转换

安装

typescript-type-def添加为你的Cargo.toml中的依赖:

[dependencies]
typescript-type-def = "0.6"

基本用法

1. 为结构体生成TypeScript定义

use typescript_type_def::TypeDef;

#[derive(TypeDef)]
struct User {
    id: u32,
    username: String,
    email: String,
    is_active: bool,
    preferences: Vec<String>,
}

这会生成对应的TypeScript接口:

interface User {
    id: number;
    username: string;
    email: string;
    is_active: boolean;
    preferences: string[];
}

2. 为枚举生成TypeScript定义

#[derive(TypeDef)]
enum Status {
    Active,
    Inactive,
    Banned,
}

生成TypeScript联合类型:

type Status = "Active" | "Inactive" | "Banned";

3. 复杂类型示例

#[derive(TypeDef)]
struct ApiResponse<T> {
    success: bool,
    data: Option<T>,
    error: Option<String>,
    metadata: HashMap<String, serde_json::Value>,
}

生成TypeScript泛型接口:

interface ApiResponse<T> {
    success: boolean;
    data?: T;
    error?: string;
    metadata: { [key: string]: any };
}

高级配置

自定义类型名称

#[derive(TypeDef)]
#[typescript(name = "UserDTO")]
struct User {
    // ...
}

忽略字段

#[derive(TypeDef)]
struct User {
    id: u32,
    #[typescript(skip)]
    password_hash: String,
    // ...
}

自定义类型转换

#[derive(TypeDef)]
struct Custom {
    #[typescript(type = "string")]
    uuid: Uuid,
}

生成TypeScript文件

你可以创建一个构建脚本(build.rs)来自动生成TypeScript定义文件:

use std::fs::File;
use std::io::Write;
use typescript_type_def::{export_types, DefinitionFile};

fn main() {
    let mut file = DefinitionFile::new();
    export_types![
        User,
        Status,
        ApiResponse
    ]
    .to_file(&mut file)
    .unwrap();

    let ts = file.to_string();
    File::create("types.d.ts")
        .and_then(|mut f| f.write_all(ts.as_bytes()))
        .unwrap();
}

完整示例demo

下面是一个完整的示例,展示如何使用typescript-type-def生成TypeScript类型定义:

  1. 首先在Cargo.toml中添加依赖:
[dependencies]
typescript-type-def = "0.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "0.8", features = ["serde", "v4"] }
  1. 创建Rust结构体和枚举:
use serde::Serialize;
use typescript_type_def::TypeDef;
use std::collections::HashMap;
use uuid::Uuid;

// 用户结构体
#[derive(TypeDef, Serialize)]
struct User {
    id: u32,
    username: String,
    email: String,
    #[typescript(skip)]  // 跳过密码字段
    password_hash: String,
    #[typescript(type = "string")]  // 自定义UUID类型为字符串
    user_uuid: Uuid,
    roles: Vec<Role>,
}

// 用户角色枚举
#[derive(TypeDef, Serialize)]
enum Role {
    Admin,
    Moderator,
    User,
}

// API响应泛型结构体
#[derive(TypeDef, Serialize)]
struct ApiResponse<T> {
    success: bool,
    data: Option<T>,
    error: Option<String>,
    metadata: HashMap<String, serde_json::Value>,
}
  1. 创建build.rs生成TypeScript定义文件:
use std::fs::File;
use std::io::Write;
use typescript_type_def::{export_types, DefinitionFile};

fn main() {
    let mut file = DefinitionFile::new();
    
    // 导出所有需要生成TypeScript定义的类型
    export_types![
        User,
        Role,
        ApiResponse
    ]
    .to_file(&mut file)
    .unwrap();

    // 将生成的TypeScript定义写入文件
    let ts = file.to_string();
    File::create("src/types.d.ts")
        .and_then(|mut f| f.write_all(ts.as_bytes()))
        .unwrap();
}
  1. 生成的types.d.ts文件内容:
interface User {
    id: number;
    username: string;
    email: string;
    user_uuid: string;
    roles: Role[];
}

type Role = "Admin" | "Moderator" | "User";

interface ApiResponse<T> {
    success: boolean;
    data?: T;
    error?: string;
    metadata: { [key: string]: any };
}

注意事项

  1. 确保你的Rust类型实现了serde::Serialize,因为typescript-type-def依赖serde的类型系统
  2. 复杂生命周期或泛型可能会有特殊处理需求
  3. 某些Rust特有类型(如Duration)需要自定义转换

这个工具可以大幅减少在Rust后端和TypeScript前端之间保持类型同步的工作量,特别适合全栈Rust开发者使用。

回到顶部