Rust类型安全与TypeScript集成库specta-typescript的使用,实现Rust到TypeScript的高效类型导出与同步

以下是关于Rust类型安全与TypeScript集成库specta-typescript的使用,实现Rust到TypeScript的高效类型导出与同步的完整示例:

首先安装specta-typescript库:

cargo add specta-typescript

或添加到Cargo.toml:

specta-typescript = "0.0.9"

完整示例代码:

use specta::{specta, Type};
use specta_typescript::export_ts;

// 定义一个Rust结构体
#[derive(Type)]
struct User {
    id: String,
    name: String,
    age: u32,
    is_active: bool,
}

// 定义一个Rust枚举
#[derive(Type)]
enum Status {
    Active,
    Inactive,
    Suspended(String),
}

// 定义一个Rust函数
#[specta]
fn get_user(id: String) -> Result<User, String> {
    Ok(User {
        id,
        name: "John Doe".to_string(),
        age: 30,
        is_active: true,
    })
}

fn main() {
    // 导出TypeScript类型定义到文件
    export_ts("src/types.ts", &[
        &User::definition(),
        &Status::definition(),
        &get_user::__specta__()
    ]).unwrap();
    
    println!("TypeScript types exported successfully!");
}

生成对应的TypeScript类型文件(src/types.ts):

export type User = {
    id: string;
    name: string;
    age: number;
    is_active: boolean;
};

export type Status = "Active" | "Inactive" | { "Suspended": string };

export function get_user(id: string): Promise<Result<User, string>>;

关键点说明:

  1. #[derive(Type)] 宏用于标记需要导出到TypeScript的Rust类型
  2. #[specta] 宏用于标记需要导出到TypeScript的Rust函数
  3. export_ts 函数将类型定义导出到指定文件
  4. 支持Rust的基本类型、结构体、枚举、Result等复杂类型

这个示例展示了如何:

  • 在Rust中定义类型安全的模型
  • 使用specta-typescript自动生成TypeScript类型定义
  • 保持前端和后端类型系统的同步
  • 减少手动维护类型定义的工作量

完整示例demo:

// src/main.rs
use specta::{specta, Type};
use specta_typescript::export_ts;

// 用户数据结构体
#[derive(Type, Debug)]
struct User {
    id: String,
    username: String,
    email: String,
    roles: Vec<Role>,
}

// 用户角色枚举
#[derive(Type, Debug)]
enum Role {
    Admin,
    User,
    Guest,
}

// API响应结构体
#[derive(Type, Debug)]
struct ApiResponse<T> {
    code: u32,
    message: String,
    data: Option<T>,
}

// 获取用户信息的API函数
#[specta]
fn fetch_user(id: String) -> ApiResponse<User> {
    ApiResponse {
        code: 200,
        message: "Success".to_string(),
        data: Some(User {
            id,
            username: "rustacean".to_string(),
            email: "user@example.com".to_string(),
            roles: vec![Role::User],
        }),
    }
}

fn main() {
    // 导出所有类型定义到types.ts文件
    export_ts("src/bindings/types.ts", &[
        &User::definition(),
        &Role::definition(),
        &ApiResponse::<User>::definition(),
        &fetch_user::__specta__()
    ]).unwrap();
    
    println!("TypeScript类型定义已成功导出!");
}

生成的TypeScript类型文件(src/bindings/types.ts):

export type User = {
    id: string;
    username: string;
    email: string;
    roles: Array<Role>;
};

export type Role = "Admin" | "User" | "Guest";

export type ApiResponse<T> = {
    code: number;
    message: string;
    data: T | null;
};

export function fetch_user(id: string): Promise<ApiResponse<User>>;

这个完整示例展示了:

  1. 更复杂的嵌套类型导出(结构体包含枚举)
  2. 泛型结构体的处理(ApiResponse<T>)
  3. 数组类型的自动转换(Vec<Role> → Array<Role>)
  4. 完整的Rust到TypeScript类型同步流程

1 回复

Rust类型安全与TypeScript集成库specta-typescript的使用

介绍

specta-typescript是一个强大的Rust库,用于将Rust类型安全地导出到TypeScript,实现Rust后端与TypeScript前端之间的类型同步。它通过过程宏自动生成TypeScript类型定义,确保两端类型系统的一致性。

主要特点:

  • 零运行时开销
  • 支持大多数Rust标准库类型
  • 可自定义类型导出
  • 与serde兼容
  • 支持枚举、结构体、元组等复杂类型

安装

在Cargo.toml中添加依赖:

[dependencies]
specta = { version = "1.0", features = ["typescript"] }
serde = { version = "1.0", features = ["derive"] }

基本使用

1. 导出简单结构体

use specta::{Type, export::typescript};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Type)]
struct User {
    id: String,
    name: String,
    age: u32,
    is_active: bool,
}

fn main() {
    // 导出单个类型
    let ts_code = typescript::export::<User>().unwrap();
    println!("{}", ts_code);
}

输出TypeScript类型:

export interface User {
    id: string
    name: string
    age: number
    is_active: boolean
}

2. 导出多个类型

use specta::{Type, export::typescript};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Type)]
struct Post {
    id: String,
    title: String,
    content: String,
    author: User,  // 使用前面定义的User类型
}

fn main() {
    // 导出多个类型
    let ts_code = typescript::export::<(User, Post)>().unwrap();
    println!("{}", ts_code);
}

3. 枚举类型导出

#[derive(Serialize, Deserialize, Type)]
enum Status {
    Active,
    Inactive,
    Suspended,
}

#[derive(Serialize, Deserialize, Type)]
enum Message {
    Text(String),
    Image { url: String, width: u32, height: u32 },
    SystemNotification,
}

导出的TypeScript:

export type Status = "Active" | "Inactive" | "Suspended"

export type Message = 
    | { type: "Text", content: string }
    | { type: "Image", url: string, width: number, height: number }
    | { type: "SystemNotification" }

高级用法

1. 自定义类型名称

#[derive(Serialize, Deserialize, Type)]
#[specta(rename = "TSUser")]
struct User {
    #[specta(rename = "userId")]
    id: String,
    // ...
}

2. 忽略字段

#[derive(Serialize, Deserialize, Type)]
struct SensitiveData {
    public_info: String,
    #[specta(skip)]
    private_key: String,
}

3. 可选字段

#[derive(Serialize, Deserialize, Type)]
struct Profile {
    username: String,
    #[specta(optional)]
    avatar_url: Option<String>,
}

4. 导出到文件

use specta::{export::typescript, Type};
use std::fs;

fn main() {
    let ts_code = typescript::export::<(User, Post, Status)>().unwrap();
    fs::write("src/bindings.ts", ts_code).unwrap();
}

与前端集成

在前端项目中,可以直接导入生成的类型定义:

import { User, Post } from './bindings';

async function fetchUser(id: string): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
}

最佳实践

  1. 在Rust后端项目中创建一个专门的模块处理类型导出
  2. 设置构建脚本自动生成类型定义文件
  3. 将类型定义文件放在前端项目容易访问的位置
  4. 考虑使用cargo watch自动重新生成类型定义

完整示例Demo

这是一个完整的Rust后端与TypeScript前端集成的示例:

后端Rust代码 (src/main.rs):

use specta::{Type, export::typescript};
use serde::{Serialize, Deserialize};

// 定义API响应结构体
#[derive(Serialize, Deserialize, Type)]
#[specta(rename = "ApiResponse")]  // 自定义TypeScript中的类型名称
pub struct ApiResponse<T> {
    success: bool,
    data: T,
    #[specta(optional)]  // 标记为可选字段
    message: Option<String>,
}

// 定义用户数据结构体
#[derive(Serialize, Deserialize, Type)]
pub struct User {
    #[specta(rename = "userId")]  // 自定义字段名称
    id: String,
    username: String,
    #[specta(optional)]
    email: Option<String>,
    role: UserRole,
}

// 定义用户角色枚举
#[derive(Serialize, Deserialize, Type)]
pub enum UserRole {
    Admin,
    Moderator,
    User,
    #[specta(rename = "GuestUser")]  // 自定义枚举变体名称
    Guest,
}

// 导出类型到文件
fn export_types() {
    let ts_code = typescript::export::<(ApiResponse<User>, User, UserRole)>()
        .expect("Failed to generate typescript types");
    
    std::fs::write("../frontend/src/types/generated.d.ts", ts_code)
        .expect("Failed to write typescript types to file");
}

fn main() {
    export_types();
    println!("TypeScript类型定义已生成并写入文件");
}

生成的前端TypeScript类型定义 (frontend/src/types/generated.d.ts):

export interface ApiResponse<T> {
    success: boolean
    data: T
    message?: string
}

export interface User {
    userId: string
    username: string
    email?: string
    role: UserRole
}

export type UserRole = "Admin" | "Moderator" | "User" | "GuestUser"

前端TypeScript使用示例 (frontend/src/api.ts):

import { ApiResponse, User } from './types/generated';

async function fetchUserProfile(userId: string): Promise<ApiResponse<User>> {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
}

async function updateUserProfile(user: Partial<User>): Promise<ApiResponse<User>> {
    const response = await fetch('/api/users', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(user),
    });
    return response.json();
}

这个完整示例展示了:

  1. 如何在Rust中定义复杂类型(包括结构体、枚举)
  2. 使用specta的自定义选项(重命名、可选字段等)
  3. 将类型导出到前端项目
  4. 在前端TypeScript代码中使用生成的类型

specta-typescript极大地简化了全栈开发中的类型同步工作,减少了手动维护类型定义的工作量和潜在错误,是Rust+TypeScript全栈开发的强大工具。

回到顶部