如何生成 TypeScript 声明?

解读

在国内 Rust 岗位面试中,面试官问“如何生成 TypeScript 声明”并不是考 TypeScript 语法,而是考察候选人是否具备把 Rust 代码暴露给前端生态的实战经验。具体场景包括:

  1. 公司用 Rust 写核心算法,通过 WebAssembly 嵌入到 React/Vue 项目;
  2. 后端用 Rust 写高性能微服务,需要给 Node/BFF 层提供类型安全的 FFI 接口;
  3. 开源库要同时发布 npm 包,必须附带 .d.ts 声明文件。
    回答时务必围绕自动化、可维护、零手工这三个国内工程落地最看重的指标展开,否则会被认为“只能写 Demo,上不了生产线”。

知识点

  1. wasm-bindgentypescript 特性:在 Cargo.toml 里打开 wasm-bindgen = { version = "0.x", features = ["typescript"] },编译器会自动在 pkg/ 目录下生成与 Rust 签名一一对应的 .d.ts 文件。
  2. ts-rs 宏:在 Rust 侧给结构体打 #[derive(TS)],配合 #[ts(export)] 直接输出 .d.ts,适合非 WASM 的 FFI 场景(如 NAPI-RS、Neon)。
  3. napi-rstype-def 命令:运行 napi build --platform --release --dts,一键生成完整声明,并自动把类型写入 index.d.ts,与 npm 包无缝集成。
  4. serde-wasm-bindgenskip-typescript 注解:当 Rust 结构体里包含 HashMap<String, serde_json::Value> 这类动态类型时,用 #[wasm_bindgen(skip_typescript)] 手动标注,避免生成 any 破坏类型安全。
  5. 国内镜像加速:在 .cargo/config.toml 里把 wasm-bindgen-cli 的下载源换成 rsproxy.cn,否则 CI 会因为 GitHub 抽风导致构建失败,这是国内落地的隐藏扣分点
  6. 版本对齐策略wasm-bindgenwasm-pack 的主版本必须锁定一致,否则会出现“生成的 .d.ts 里多一个泛型参数,前端项目瞬间爆红”的典型事故。

答案

生产级做法分三步:
第一步,在 Cargo.toml 中启用 wasm-bindgen 的 typescript 特性并锁定版本,例如

[dependencies]
wasm-bindgen = { version = "=0.2.92", features = ["typescript"] }

第二步,使用 wasm-pack 构建并指定目标为 bundler,命令行执行

wasm-pack build --target bundler --out-dir pkg --release

构建完成后,pkg/your_crate.d.ts 已自动生成,且与 Rust 函数签名完全同步;
第三步,在 package.json 中把 types 字段指向该文件,并配置 .npmignorepkg/ 以外的 Rust 源码排除,确保发布到公司私有 npm 源时体积小于 300 kB。
若项目是 Node native 扩展而非 WASM,则改用 napi-rs:

napi build --platform --release --dts

生成的 index.d.ts 直接位于项目根目录,前端团队无需任何额外配置即可 import { sum } from 'your-rust-addon' 并获得完整类型提示。
整个流程必须写进 GitHub Actions 或 GitLab CI,做到 tag 一推自动发布 npm 包,这是国内面试官最认可的“工程化闭环”。

拓展思考

  1. 如何把 Rust 的泛型 Trait 导出到 TypeScript?
    目前 wasm-bindgen 只能导出具体实例化类型,因此需要在 Rust 侧写类型擦除层:先封装成 struct JsFoo { inner: Box<dyn Trait> },再打 #[wasm_bindgen] 导出,TypeScript 侧看到的只是一个 class JsFoo,内部实现细节完全隐藏。
  2. 国内网络环境下 CI 频繁失败如何兜底?
    wasm-bindgen-cli 的二进制提前缓存到自建 MinIO 或阿里云 OSS,在 Dockerfile 里用 RUN curl -o /usr/local/bin/wasm-bindgen https://your-oss.wasm-bindgen-0.2.92-x86_64-unknown-linux-musl.tar.gz 拉取,彻底摆脱 GitHub 下载超时;同时把 wasm-pack 版本锁进 rust-toolchain.tomlpackageMetadata 字段,保证回滚可复现。
  3. 当 TypeScript 声明过大导致 VSCode 卡死怎么办?
    tsconfig.json 里开启 skipLibCheck: true 只是临时方案,正确做法是把巨型结构体拆成多个 crate,每个 crate 只导出最小接口,再用 workspace 聚合;这样每个 .d.ts 文件不超过 5000 行,既保持类型安全,又不会把前端开发机拖垮。