Laravel Orbit TypeScript 类型生成
解读
在国内一线/二线互联网公司的 PHP 岗位面试中,面试官提出“Laravel Orbit TypeScript 类型生成”并不是想让你背诵 Orbit 的 README,而是考察三件事:
- 你是否真的用 Laravel 做过后端驱动的 SSR/SPA 混合项目;
- 你是否能把后端模型、API 契约与前端 TypeScript 类型自动对齐,解决“接口一改,前端爆红”的痛点;
- 你是否理解国内主流合规、安全、性能要求(如内网离线部署、禁止外网 CDN、TS 类型必须本地化生成)。
因此,题目背后真正想问的是:
“如何在 Laravel 侧利用 Orbit(或同类方案)一键生成 TypeScript 类型定义,并保证在持续集成、微服务、多仓库场景下长期可维护?”
知识点
- Laravel Orbit 本质:把 Eloquent Model 当作“数据源”,通过 JSON Schema / TypeScript AST 反向生成 .d.ts,而不是传统“PHP 写接口→前端手动对齐”。
- 国内落地差异:
- 内网 GitLab CI 无法拉外网包,必须将 @orbitjs/cli 等工具预打包进公司私有 Nexus;
- 类型文件需符合集团前端规约(统一用 enum 而非 const enum,禁止 any,必须带 JSDoc 中文注释);
- 需要支持多版本并行:老版本 H5 走 v1 类型,小程序走 v2 类型,生成目录需隔离。
- 关键技术栈:
- 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 则阻断合并,防止线上事故。
- 性能与安全:
- 生成阶段放在 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 秒,完全符合公司内网离线、安全合规的要求。”
拓展思考
- 如果集团里还有 Java、Go 微服务,如何统一用 JSON Schema 中心仓库做跨语言类型对齐?是否需要把 Orbit 生成的 Schema 上传到 Apollo/Consul,再让各语言消费?
- 当模型字段达到 1000+ 时,全量生成会导致前端 node_modules 体积膨胀,如何按业务域拆分子包(@company/order-types、@company/user-types)并做按需加载?
- 国内小程序对包体积极度敏感,能否把类型文件在上线前用 ts-transformer 剔除仅开发时使用的属性,再压缩成 .d.ts.gz,运行时动态解压?
- 若公司要求“零 Node 依赖”,能否用 PHP 本身解析 TypeScript AST(如通过 swc-php 扩展),实现纯 PHP 侧生成 .d.ts,从而摆脱 node 环境?
- 未来如果 Laravel 转向原生支持 TypeScript,官方可能会提供 #[TypeScript] 属性,届时如何设计平滑迁移方案,既兼容老 Orbit 属性,又能利用 PHP 的 JIT 缓存提升扫描性能?