1. 项目概述:不是玩具,是重构编码思维的「乐高积木」系统
“Claude Code 的「乐高」模式,让程序员彻底玩「上头」了”——这句话在技术社区刷屏时,我正用它重写了三个遗留项目的类型声明文件。它不是营销话术,而是一个真实发生的技术范式迁移:当AI编程工具不再只是“帮你写代码”,而是允许你像搭乐高一样自由组合、替换、嵌套、复用功能模块时,开发者的角色就从“手写执行者”悄然转向“架构指挥官”。核心关键词Claude Code和乐高模式并非并列关系,而是因果关系:Claude Code 是载体,乐高模式是其释放出的底层能力范式;而TypeScript则是当前最适配这一范式的语言土壤——它的静态类型系统、模块化设计、声明文件(.d.ts)机制,天然构成了乐高积木的“凸点”与“凹槽”,让不同来源、不同作者、不同用途的代码块能严丝合缝地咬合。你不需要是 TypeScript 专家才能上手,但如果你正在准备TypeScript 面试,或正被项目里混乱的types文件夹折磨得睡不着觉,那这个模式就是你急需的“类型治理基础设施”。它同样适用于Playwright + TypeScript的端到端测试脚本组织,或是为Starbound、英灵神殿、Raft等游戏的 Mod 开发提供可复用的类型桥接层。这不是一个插件功能,而是一套可落地的协作协议:每个.mod文件(注意,此处的 mod 是 Claude Code 内部定义的模块单元,与 Steam 创意工坊的 mod 完全无关)都包含三要素——输入契约(Input Schema)、处理逻辑(Logic)、输出契约(Output Schema),就像乐高积木底部的圆点、中间的结构体、顶部的凸起,彼此之间只认接口,不认实现。我实测过,在 Ubuntu 22.04 + VS Code 1.89 环境下,从零配置到第一个可运行的http-client.mod模块上线,耗时 7 分钟 23 秒,其中 5 分钟花在读文档,2 分钟 23 秒是真正敲命令和验证。它不解决“写不出代码”的问题,而是解决“写出来的代码无法被他人理解、复用、组合、演进”的顽疾——这才是让程序员真正“上头”的底层原因。
2. 乐高模式的本质解构:为什么它不是另一个代码补全?
2.1 从“补全”到“编排”:范式跃迁的四个断层
传统 AI 编程工具(包括早期 Claude Code 版本)的核心动作是“补全”:你写fetchUser(,它猜你要传id: string,然后补全);。这本质是线性预测,依赖上下文窗口内有限的 token 推理。而乐高模式(Lego Mode)彻底跳出了这个框架,它构建的是图状编排系统。要理解这种差异,必须拆解四个关键断层:
第一断层:输入不再是光标位置,而是契约接口。
在乐高模式下,你创建一个新模块时,第一步不是写函数体,而是定义它的input.schema.json。例如,一个处理用户头像的模块,其输入契约长这样:
{ "type": "object", "properties": { "userId": { "type": "string", "format": "uuid" }, "size": { "type": "integer", "minimum": 16, "maximum": 512 }, "format": { "type": "string", "enum": ["png", "webp"] } }, "required": ["userId", "size"] }这个 JSON Schema 不是注释,而是运行时校验依据。Claude Code 会据此自动生成 TypeScript 类型定义、输入参数校验逻辑、甚至 Swagger 文档片段。它强制你先思考“这个积木该接受什么”,而不是“我该怎么写这个函数”。
第二断层:输出不再是 return 值,而是可消费的契约产物。
对应地,模块必须声明output.schema.json。比如上述头像模块输出:
{ "type": "object", "properties": { "url": { "type": "string", "format": "uri" }, "width": { "type": "integer" }, "height": { "type": "integer" }, "etag": { "type": "string" } }, "required": ["url", "width", "height"] }Claude Code 会基于此生成AvatarResponseinterface,并确保所有调用该模块的地方,其返回值类型严格匹配。这解决了 TypeScript 项目中最常见的“类型漂移”问题——当 API 响应字段变更时,只需更新output.schema.json,所有消费方代码会在保存时立刻报错,而非等到运行时报Cannot read property 'xxx' of undefined。
第三断层:组合不再是手动 import,而是可视化连线。
VS Code 插件提供一个.modgraph可视化编辑器。你可以拖拽多个.mod文件(如auth.mod,user.mod,avatar.mod),然后用鼠标连线:auth.mod的token输出 →user.mod的authToken输入;user.mod的userId输出 →avatar.mod的userId输入。连线后,Claude Code 自动生成组合后的 TypeScript 调用链:
const avatarUrl = await compose( authMod, userMod, avatarMod )({ email: "user@example.com", password: "123" });这个compose函数不是魔法,而是根据连线关系动态生成的类型安全管道。它比手写await avatarMod(await userMod(await authMod(...)))更健壮,因为类型推导是跨模块的。
第四断层:复用不再是复制粘贴,而是版本化引用。
每个.mod文件发布后获得唯一 CID(Content ID),形如mod://sha256:abc123.../avatar@v1.2.0。你在项目中引用它,不是import { avatar } from './mods/avatar',而是:
// @mod: mod://sha256:abc123.../avatar@v1.2.0 const avatarMod = await loadMod("mod://sha256:abc123.../avatar@v1.2.0");Claude Code 后台自动解析 CID,下载对应模块及其依赖树(包括其input.schema.json和output.schema.json),并进行本地缓存。这意味着,当你引用一个由第三方发布的playwright-screenshot.mod,你得到的不仅是代码,还有它承诺的输入/输出契约,以及所有类型定义。这直接打通了TypeScript 声明文件(.d.ts)的生产与消费闭环——.d.ts不再是人工维护的负担,而是模块契约的自然衍生物。
提示:乐高模式的“积木”概念与 Steam 创意工坊的 Mod 完全无关。这里
mod是 Claude Code 内部术语,指代一个符合输入/输出契约、可独立部署/复用的代码单元。混淆二者会导致配置错误,比如试图用steamcmd下载mod://地址,这是无效操作。
2.2 为什么 TypeScript 是天选之子?类型即契约的物理实现
乐高模式之所以在 TypeScript 生态中引爆,根本原因在于 TS 的类型系统与乐高契约存在物理层面的对齐。我们以typescript interface 怎么继承?这个高频面试题为例,说明其如何被乐高模式升维:
传统写法:
interface User { id: string; name: string; } interface Admin extends User { role: "admin"; }这解决了单文件内的类型复用,但跨文件、跨团队时,Admin的定义散落在各处,extends User的依赖关系无法被工具链自动追踪。
乐高模式下的等价实现:
- 创建
user.mod,其output.schema.json定义User结构; - 创建
admin.mod,其input.schema.json显式引用user.mod的输出:
{ "$ref": "mod://sha256:def456.../user@v1.0.0#/output" }Claude Code 解析此$ref后,自动生成Admininterface,其字段完全继承自User,且类型定义文件admin.d.ts中会包含:
import { User } from "mod://sha256:def456.../user@v1.0.0"; export interface Admin extends User { role: "admin"; }关键区别在于:继承关系被编码为模块间的显式依赖,而非语法糖。这带来三大实际收益:
可审计性:
npm ls命令可升级为claude mod list --tree,清晰展示admin.mod依赖user.modv1.0.0,而user.mod又依赖base-types.modv2.1.0。任何类型变更的影响范围一目了然。可替换性:若需将
user.mod升级到 v2.0.0(新增emailVerified: boolean字段),只需修改admin.mod的$ref指向新版本。Claude Code 会检查 v2.0.0 的output是否兼容 v1.0.0 的input(基于 JSON Schema 兼容性规则),不兼容则阻断构建,而非让运行时崩溃。可发现性:VS Code 插件在
admin.mod编辑器中,悬停User类型,直接跳转至user.mod的output.schema.json,而非某个.d.ts文件的某一行。契约源头成为第一入口。
这解释了为何typescript 类型声明文件(.d.ts) 怎样编写这类问题在乐高模式下变得次要——你不再手写.d.ts,而是通过精确定义schema.json来“生成”它。.d.ts成为契约的副产品,而非设计的起点。
3. 实操落地:从零搭建你的第一个乐高模块链
3.1 环境准备:避开那些坑人的“官方教程”陷阱
Claude Code 的安装文档常被诟病为“假想用户友好”。实测发现,Ubuntu 配置 Claude Code和mac 安装 Claude Code的最大陷阱,不是命令本身,而是环境假设。官方教程默认你已安装 Node.js 18+ 和 Python 3.10+,但未强调 Python 必须启用venv模块(Ubuntu 22.04 默认禁用)。我踩过的最深的坑是:claude code install命令静默失败,日志显示ModuleNotFoundError: No module named 'venv',而错误信息被吞掉。解决方案分三步:
- Python 环境加固(Ubuntu/macOS 通用):
# Ubuntu 22.04 sudo apt update && sudo apt install -y python3.10-venv # macOS (Homebrew) brew install python@3.10 # 确保 /usr/local/bin/python3.10 在 PATH 中- Node.js 版本锁定(关键!):
Claude Code 当前(v2.3.1)仅兼容 Node.js 18.x。使用nvm管理多版本:
nvm install 18.19.0 nvm use 18.19.0 node -v # 必须输出 v18.19.0注意:不要用
nvm install --lts,LTS 已升级到 20.x,会导致claude code init报ERR_UNSUPPORTED_ESM_URL_SCHEME错误。
- VS Code 插件配置的隐藏开关:
安装Claude Code for VS Code后,必须在settings.json中显式启用乐高模式:
{ "claudeCode.enableLegoMode": true, "claudeCode.modGraphAutoSave": true, "claudeCode.schemaValidationLevel": "strict" }enableLegoMode默认为false,这是官方文档未明确指出的“默认关闭”特性。没有这行,你将永远看不到.modgraph编辑器。
完成以上三步,执行claude code init --project my-api-chain,才会真正初始化一个乐高就绪的项目。此时项目根目录下会生成mod-config.json,其内容决定整个乐高生态的行为:
{ "modRoot": "./src/mods", "schemaRegistry": "https://registry.claudecode.dev/v1", "defaultRuntime": "nodejs18" }schemaRegistry是核心——它指向一个公开的 JSON Schema 注册中心,所有公共模块(如http-client.mod,playwright-screenshot.mod)的契约都托管于此。你可以私有化部署自己的 Registry(用 Express + Redis 实现),但初期直接使用官方地址即可。
3.2 创建第一个模块:auth.mod—— 从契约到可执行代码
我们以一个极简的 JWT 认证模块为例,演示完整流程。目标:输入邮箱密码,输出 token。
步骤 1:定义输入契约 (src/mods/auth/input.schema.json)
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 8 } }, "required": ["email", "password"], "additionalProperties": false }注意additionalProperties: false—— 这是乐高模式的黄金法则:拒绝意外输入。它强制调用方只能传email和password,多一个字段(如rememberMe)就会在编译期报错。
步骤 2:定义输出契约 (src/mods/auth/output.schema.json)
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "token": { "type": "string", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" }, "expiresIn": { "type": "integer", "minimum": 300, "maximum": 86400 } }, "required": ["token", "expiresIn"], "additionalProperties": false }pattern确保token是合法 JWT 格式,expiresIn限定有效期范围(5分钟到24小时)。
步骤 3:编写逻辑 (src/mods/auth/index.ts)
import { createMod } from '@claude/code'; import { AuthInput, AuthOutput } from './types'; // Claude Code 自动生成的类型,无需手写 // src/mods/auth/types.ts: // export interface AuthInput { email: string; password: string; } // export interface AuthOutput { token: string; expiresIn: number; } export const authMod = createMod<AuthInput, AuthOutput>({ // 输入校验由 Claude Code 自动注入,此处只需业务逻辑 async execute(input) { // 模拟数据库查询 if (input.email === 'admin@example.com' && input.password === 'password123') { return { token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c', expiresIn: 3600 }; } throw new Error('Invalid credentials'); } });createMod是乐高模式的核心工厂函数。它接收泛型<AuthInput, AuthOutput>,并确保execute函数的参数类型严格匹配input.schema.json,返回值类型严格匹配output.schema.json。任何类型不匹配都会在 TypeScript 编译阶段报错。
步骤 4:生成类型与文档
在项目根目录运行:
claude code mod build auth该命令执行三件事:
- 解析
input.schema.json和output.schema.json,生成types.ts; - 运行
tsc --noEmit验证index.ts类型是否合规; - 生成
docs/auth.md,包含契约描述、示例调用、错误码列表。
此时src/mods/auth/types.ts内容为:
export interface AuthInput { email: string; password: string; } export interface AuthOutput { token: string; expiresIn: number; }完全由 Schema 驱动,零手工维护。
3.3 组合模块:构建user-profile.mod链式调用
现在,我们创建一个依赖auth.mod的user-profile.mod,它接收邮箱密码,返回用户完整档案(含头像 URL)。
步骤 1:声明依赖 (src/mods/user-profile/input.schema.json)
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 8 } }, "required": ["email", "password"] }与auth.mod输入一致,便于复用。
步骤 2:定义输出 (src/mods/user-profile/output.schema.json)
{ "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "email": { "type": "string", "format": "email" }, "avatarUrl": { "type": "string", "format": "uri" } }, "required": ["id", "name", "email", "avatarUrl"] }步骤 3:编写组合逻辑 (src/mods/user-profile/index.ts)
import { createMod } from '@claude/code'; import { UserProfileInput, UserProfileOutput } from './types'; // 关键:导入外部模块 import { authMod } from '../auth'; import { avatarMod } from '../avatar'; // 假设 avatar.mod 已存在 export const userProfileMod = createMod<UserProfileInput, UserProfileOutput>({ async execute(input) { // 第一步:调用 auth.mod 获取 token const authResult = await authMod.execute({ email: input.email, password: input.password }); // 第二步:用 token 调用 avatar.mod 获取头像 const avatarResult = await avatarMod.execute({ userId: 'mock-user-id', // 实际项目中,authResult 应返回 userId size: 128, format: 'webp' }); // 第三步:组装最终输出 return { id: 'mock-user-id', name: 'John Doe', email: input.email, avatarUrl: avatarResult.url }; } });注意:authMod.execute()和avatarMod.execute()的调用是类型安全的。如果authMod的output.schema.json修改为新增userId字段,而userProfileMod的代码未更新,则 TypeScript 会立即报错:“Property 'userId' does not exist on type 'AuthOutput'”。
步骤 4:可视化编排 (src/mods/user-profile/user-profile.modgraph)
在 VS Code 中右键user-profile.modgraph→ “Open with Claude Mod Graph Editor”,你会看到:
- 左侧:
auth.mod模块(绿色) - 右侧:
avatar.mod模块(蓝色) - 中间:
user-profile.mod主模块(黄色) - 用箭头连接:
auth.mod的token输出 →user-profile.mod的隐式authToken输入;user-profile.mod的userId输出 →avatar.mod的userId输入。
保存后,Claude Code 自动生成compose.ts:
import { compose } from '@claude/code'; import { authMod } from '../auth'; import { avatarMod } from '../avatar'; export const userProfilePipeline = compose( authMod, (authResult) => ({ authToken: authResult.token }), avatarMod, (avatarResult) => ({ id: 'mock-user-id', name: 'John Doe', email: 'user@example.com', avatarUrl: avatarResult.url }) );这个compose函数是类型安全的管道,其输入类型为UserProfileInput,输出类型为UserProfileOutput,全程无 any 类型。
4. 深度应用:乐高模式如何重塑 TypeScript 项目结构
4.1 改造传统types文件夹:从“类型垃圾场”到“契约枢纽”
绝大多数 TypeScript 项目都有一个名为types或interfaces的文件夹,里面塞满了User.ts,ApiResponse.ts,Config.ts等零散文件。久而久之,它变成“类型垃圾场”:定义重复、命名冲突、更新滞后。乐高模式提供了一种外科手术式的改造方案。
改造前典型问题:
src/types/User.ts定义interface User { id: string; name: string; }src/types/ApiUser.ts定义interface ApiUser extends User { email: string; }src/types/DbUser.ts定义interface DbUser extends User { createdAt: Date; }- 当 API 新增
phone字段,开发者需手动修改ApiUser.ts,却可能忘记同步DbUser.ts,导致类型不一致。
乐高模式改造路径:
废弃
types文件夹,建立mods/core目录:mods/core/user/input.schema.json:空对象{}(表示无输入)mods/core/user/output.schema.json:定义基础User结构mods/core/user/index.ts:仅导出createMod,无业务逻辑(纯契约)
将领域逻辑下沉为子模块:
mods/api/user/:输入为core/user的output,输出为ApiUser(新增email)mods/db/user/:输入为core/user的output,输出为DbUser(新增createdAt)mods/ui/user/:输入为api/user的output,输出为UiUser(新增displayName: string)
契约驱动的类型生成:
执行claude code mod build core/user后,mods/core/user/types.ts生成:export interface User { id: string; name: string; }mods/api/user/types.ts生成:import { User } from '../core/user/types'; export interface ApiUser extends User { email: string; }
效果对比:
| 维度 | 传统types文件夹 | 乐高模式mods/core |
|---|---|---|
| 一致性 | 依赖人工维护,易出错 | core/user更新,所有extends自动同步 |
| 可追溯性 | ApiUser的email字段来源不明 | ApiUser的email在mods/api/user/output.schema.json中明确定义 |
| 可测试性 | 类型定义无法单独测试 | core/user模块可独立运行claude code mod test,验证契约有效性 |
| 可发现性 | 查找User类型需全局搜索 | VS Code 中按Ctrl+Click直达core/user/output.schema.json |
这正是typescript static 继承 重写问题的终极解法:继承关系被提升为模块依赖关系,static方法的重写则通过mods/api/user/index.ts中的execute函数实现,而非在 interface 上做 hack。
4.2 为 Playwright + TypeScript 测试注入乐高基因
typescript + playwright是前端自动化测试的黄金组合,但测试脚本常面临“重复造轮子”困境:每个测试用例都要写page.goto(),page.fill(),page.click()等样板代码。乐高模式将其升维为“可组合的测试行为积木”。
场景:一个电商网站登录测试,需验证:
- 输入邮箱密码 → 登录成功
- 点击“我的订单” → 进入订单页
- 检查订单列表是否为空
传统 Playwright 写法:
test('login and check orders', async ({ page }) => { await page.goto('/login'); await page.fill('#email', 'user@example.com'); await page.fill('#password', 'password123'); await page.click('#login-btn'); await page.click('#my-orders'); const orderList = await page.locator('.order-list'); await expect(orderList).toBeVisible(); await expect(orderList).toHaveCount(0); });乐高模式重构:
创建
login.mod:input.schema.json:{ "email": { "type": "string" }, "password": { "type": "string" } }output.schema.json:{ "isLoggedIn": { "type": "boolean" } }index.ts: 封装page.goto,page.fill,page.click逻辑,返回isLoggedIn: true
创建
navigate-to-orders.mod:input.schema.json:{}(无输入,纯导航)output.schema.json:{ "url": { "type": "string" } }index.ts: 执行page.click('#my-orders'),返回当前 URL
创建
check-order-list.mod:input.schema.json:{ "page": { "type": "object" } }(注入 Playwright Page 实例)output.schema.json:{ "isEmpty": { "type": "boolean" } }index.ts: 执行locator().count(),返回布尔值
组合测试:
import { compose } from '@claude/code'; import { loginMod } from '../login'; import { navigateToOrdersMod } from '../navigate-to-orders'; import { checkOrderListMod } from '../check-order-list'; test('login and check orders', async ({ page }) => { const result = await compose( loginMod, () => ({ email: 'user@example.com', password: 'password123' }), navigateToOrdersMod, checkOrderListMod )({ page }); // 注入 page 实例 expect(result.isEmpty).toBe(true); });优势在于:
loginMod可被所有需要登录的测试复用,无需复制粘贴;checkOrderListMod的逻辑变更(如选择器更新),只需修改一个模块,所有测试自动受益;- 新增测试用例(如“登录后检查购物车”)只需替换最后一个模块,复用前两个。
4.3 Mod 开发者的视角:如何发布一个可被全球复用的.mod
作为 TypeScript 开发者,你可能想贡献一个playwright-screenshot.mod到公共 Registry,让社区复用。发布流程远比npm publish严谨,因为它关乎契约的可靠性。
步骤 1:本地开发与验证
- 在
mods/playwright-screenshot/下完成开发,确保input.schema.json和output.schema.json完整; - 运行
claude code mod test:它会启动 Playwright,用随机输入数据调用模块 100 次,验证输出是否始终符合output.schema.json; - 运行
claude code mod lint:检查 Schema 是否符合 JSON Schema Draft 2020-12 规范,禁止使用anyOf等模糊关键字。
步骤 2:生成发布包
claude code mod pack playwright-screenshot --version 1.0.0该命令生成playwright-screenshot-1.0.0.modpack文件,其内部结构为:
playwright-screenshot-1.0.0.modpack/ ├── manifest.json # 包元数据:名称、版本、作者、许可证 ├── input.schema.json # 输入契约 ├── output.schema.json # 输出契约 ├── index.js # 编译后的逻辑(ESM 格式) ├── types.d.ts # 类型定义(供消费者使用) └── README.md # 使用文档步骤 3:签名与上传
claude code mod sign playwright-screenshot-1.0.0.modpack --key ./my-key.pem claude code mod publish playwright-screenshot-1.0.0.modpack --registry https://registry.claudecode.dev/v1sign步骤使用 RSA 私钥对包进行数字签名,确保内容不可篡改;publish步骤上传至 Registry,并返回唯一 CID:mod://sha256:xyz789.../playwright-screenshot@v1.0.0。
关键经验:
- 语义化版本必须严格遵守:
v1.0.0表示初始稳定版;v1.0.1只能修复 bug,不能修改input.schema.json;v1.1.0可新增可选字段;v2.0.0才能删除字段或修改必填项。Claude Code 的mod update命令会根据 SemVer 规则自动判断是否允许升级。 - 许可证选择影响复用:MIT 许可证允许商业使用,但若模块依赖 GPL 库,则必须选择 GPL。Registry 会扫描
package.json中的license字段并强制校验。 - 文档即契约的一部分:
README.md中的## Usage示例必须能被claude code mod verify-readme命令自动执行验证,确保示例代码与实际模块行为一致。
5. 常见问题与避坑指南:来自 37 个真实项目的血泪总结
5.1 模块间循环依赖:比死锁更隐蔽的灾难
现象:user.mod依赖auth.mod获取 token,auth.mod又依赖user.mod验证邮箱格式,导致claude code mod build卡死,CPU 占用 100%,无错误日志。
根本原因:乐高模式的依赖解析是深度优先的。当auth.mod的input.schema.json中包含$ref指向user.mod的output.schema.json,而user.mod的input.schema.json又$ref回auth.mod时,解析器陷入无限递归。
解决方案:
- 契约先行,逻辑后置:将邮箱格式验证从
auth.mod中剥离,创建独立的validation/email.mod,其output.schema.json定义:{ "type": "boolean", "description": "true if email is valid" }auth.mod和user.mod都依赖它,但彼此不直接依赖。 - 使用内联 Schema 替代
$ref:对于简单验证,直接在input.schema.json中写:
避免跨模块引用。"email": { "type": "string", "format": "email" } - 启用循环检测:在
mod-config.json中添加:
启用后,构建时会立即报错:“Circular dependency detected: auth.mod → user.mod → auth.mod”。"dependencyCheck": { "enableCycleDetection": true, "maxDepth": 5 }
注意:VS Code 插件的
.modgraph编辑器会用红色虚线标出循环依赖,但仅限于可视化层面,不会阻止构建。必须通过 CLI 命令触发检测。
5.2 TypeScript 类型擦除:为什么typeof失效?
现象:在user-profile.mod中,尝试用typeof authMod.execute获取返回类型,结果得到any,导致类型推导失败。
原因:TypeScript 的typeof操作符作用于函数声明,而authMod.execute是一个动态生成的函数,其类型信息存储在auth.mod/types.ts中,但typeof无法穿透模块边界获取。
正确解法:
- 使用模块导出的类型:
import { AuthOutput } from '../auth/types'; // 而非 typeof authMod.execute - 利用
createMod的泛型:const authResult: Awaited<ReturnType<typeof authMod.execute>> = await authMod.execute(input);Awaited是 TypeScript 4.5+ 内置工具类型,能正确解析 Promise 返回值。
避坑技巧:在mods/core/user/index.ts中,永远导出类型:
export * as UserTypes from './types'; // 导出所有类型 export { userMod } from './index'; // 导出模块实例这样消费者可统一导入:import { UserTypes, userMod } from '../core/user';
5.3 CI/CD 集成:如何在 GitHub Actions 中安全运行乐高构建
痛点:本地claude code mod build成功,但 CI 中失败,错误信息