Laravel Orbit TypeScript 类型生成

解读

在国内一线/二线互联网公司的 PHP 岗位面试中,面试官提出“Laravel Orbit TypeScript 类型生成”并不是想让你背诵 Orbit 的 README,而是考察三件事:

  1. 你是否真的用 Laravel 做过后端驱动的 SSR/SPA 混合项目;
  2. 你是否能把后端模型、API 契约与前端 TypeScript 类型自动对齐,解决“接口一改,前端爆红”的痛点;
  3. 你是否理解国内主流合规、安全、性能要求(如内网离线部署、禁止外网 CDN、TS 类型必须本地化生成)。

因此,题目背后真正想问的是:
“如何在 Laravel 侧利用 Orbit(或同类方案)一键生成 TypeScript 类型定义,并保证在持续集成、微服务、多仓库场景下长期可维护?”

知识点

  1. Laravel Orbit 本质:把 Eloquent Model 当作“数据源”,通过 JSON Schema / TypeScript AST 反向生成 .d.ts,而不是传统“PHP 写接口→前端手动对齐”。
  2. 国内落地差异:
    • 内网 GitLab CI 无法拉外网包,必须将 @orbitjs/cli 等工具预打包进公司私有 Nexus;
    • 类型文件需符合集团前端规约(统一用 enum 而非 const enum,禁止 any,必须带 JSDoc 中文注释);
    • 需要支持多版本并行:老版本 H5 走 v1 类型,小程序走 v2 类型,生成目录需隔离。
  3. 关键技术栈:
    • PHP 8.2+ 属性(Attribute)扫描:#[OrbitType] 标记字段是否导出;
    • Laravel Command + Symfony Process 驱动本地 node 脚本;
    • 使用 typescript-compiler-api 将 JSON Schema 转成 TS Interface,并追加自定义头部 // Code generated by Laravel-Orbit, DO NOT EDIT;
    • 结合 husky + lint-staged 在 commit 阶段自动 diff 类型文件,若出现 breaking change 则阻断合并,防止线上事故。
  4. 性能与安全:
    • 生成阶段放在 GitLab Runner 的 build 阶段,产物存入 storage/orbit/ 并打包进 Docker 镜像,避免运行时 IO;
    • 敏感字段(如身份证、手机号)用 #[OrbitIgnore] 过滤,防止类型文件泄漏到前端;
    • 对枚举值进行 MD5 摘要,确保同构性,避免“0/1/2”魔法数字扩散。

答案

“我在上一家公司用 Laravel 8 做 SaaS 商户后台,前端是 React + TypeScript。为了把后端 200+ 张表的字段类型同步到前端,我们基于 Laravel Orbit 做了二次封装,核心流程分四步:

第一步,在 Model 里用 PHP8 属性声明哪些字段需要导出,以及中文注释、枚举映射:

#[OrbitType(
    tsType: 'string',
    enum: ['active' => '启用', 'inactive' => '禁用'],
    comment: '账户状态'
)]
public string $status;

第二步,写一个 Artisan Command orbit:generate,它会:

  • 扫描所有带 OrbitType 的模型,生成中间 JSON Schema;
  • 调用本地 node 脚本(通过 Symfony Process),把 Schema 喂给 typescript-compiler-api,产出 .d.ts;
  • 同时生成一个 orbit.lock 文件,记录字段哈希,用于下次增量对比。

第三步,在 .gitlab-ci.yml 里新增一个 orbit 阶段,产物统一打到 docker.xxx-inc.com/base/php-orbit:{{CI_COMMIT_SHA}},前端通过 npm link @company/orbit-types 引用,保证版本绝对对齐。

第四步,在合并请求里用自定义的 GitLab Rule:如果 orbit.lock 出现新增必填字段,必须让 QA 回归测试对应页面,否则 MR 无法合并。

上线三个月,我们做到了‘后端改字段,前端零手动改动’,接口事故率从每月 3 次降到 0,类型文件大小控制在 120 KB 以内,构建时长增加不到 5 秒,完全符合公司内网离线、安全合规的要求。”

拓展思考

  1. 如果集团里还有 Java、Go 微服务,如何统一用 JSON Schema 中心仓库做跨语言类型对齐?是否需要把 Orbit 生成的 Schema 上传到 Apollo/Consul,再让各语言消费?
  2. 当模型字段达到 1000+ 时,全量生成会导致前端 node_modules 体积膨胀,如何按业务域拆分子包(@company/order-types、@company/user-types)并做按需加载?
  3. 国内小程序对包体积极度敏感,能否把类型文件在上线前用 ts-transformer 剔除仅开发时使用的属性,再压缩成 .d.ts.gz,运行时动态解压?
  4. 若公司要求“零 Node 依赖”,能否用 PHP 本身解析 TypeScript AST(如通过 swc-php 扩展),实现纯 PHP 侧生成 .d.ts,从而摆脱 node 环境?
  5. 未来如果 Laravel 转向原生支持 TypeScript,官方可能会提供 #[TypeScript] 属性,届时如何设计平滑迁移方案,既兼容老 Orbit 属性,又能利用 PHP 的 JIT 缓存提升扫描性能?