ECC 规则体系实战:TypeScript/JavaScript 模式规范(patterns.md)深度解读与代码实现指南
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
本文以 ECC(The agent harness performance optimization system)规则库中的 rules/typescript/patterns.md(以及其西班牙语文档 docs/es/rules/typescript/patterns.md)为核心,系统解读 ECC 为 TypeScript/JavaScript 代码生成的 Agent 所沉淀的三类核心编码模式:API 响应格式(API Response Format)、自定义 Hooks 模式(Custom Hooks Pattern)与仓储模式(Repository Pattern)。文章逐项还原每个模式的完整代码骨架,并从 ECC 仓库中已有实现与配套规则(如 console.log 检测 Hook)反推其背后的工程动机与落地方法,让读者在阅读与编写 TS/JS 代码时能直接套用这套"可被 Agent 与人工评审一致执行"的规范。
一、这份规则文档在 ECC 中的定位与作用方式
ECC 是一个面向 Claude Code、Codex、Opencode、Cursor 等编码 Agent 的"性能优化系统",通过 rules 目录为不同语言提供分门别类的规则文件,确保 Agent 在自动生成、审查代码时遵循统一范式,避免每次生成的代码风格漂移、边界处理缺失。
本文档即rules/typescript/规则组中的patterns.md。规则文件开头的 front matter 明确声明了它的作用范围:
paths: - "**/*.ts" - "**/*.tsx" - "**/*.js" - "**/*.jsx"它意味着:当 Agent 编辑或评审任意.ts/.tsx/.js/.jsx文件时,本规则自动被加载并约束输出。同时文档头部写明了它与通用规则的层级关系:
本文件扩展 common/patterns.md 中与 TypeScript/JavaScript 相关的内容。
也就是说,通用规则先定义了"做什么"(为什么要封装数据访问、为什么需要统一的响应信封),语言级规则再给出"怎么写"(具体的 TypeScript 泛型签名)。两者叠加才是完整规范。同目录下还配有一套互为补充的规则文件,构成完整的语言约束矩阵:
- rules/typescript/coding-style.md:类型/接口使用、不可变性、错误处理、
any规避等编码风格; - rules/typescript/hooks.md:编辑器/Agent Hook 侧的自动格式化与静态检查约定;
- rules/typescript/security.md 与 rules/typescript/testing.md:安全与测试侧规则。
这种"front matter 声明作用路径 + 文档互相引用 + 语言规则分层扩展"的结构,正是 ECC 让 Agent 可确定性遵循工程规范的关键机制。
二、API 响应格式:用统一信封约束所有接口
2.1 规则原文中的信封结构
patterns.md 给出的核心骨架如下:
interface ApiResponse<T> { success: boolean data?: T error?: string meta?: { total: number page: number limit: number } }这是一份对所有 API 返回值形态的强制约定。对照 common/patterns.md 中"API Response Format"一节的要求,可以还原出每个字段的设计意图:
| 字段 | 类型 | 含义与取值约束 |
|---|---|---|
success | boolean | 成功/失败状态指示器,所有响应都必须有 |
data | T \| undefined | 成功时的数据负载;失败时置空 |
error | string \| undefined | 失败时的错误消息;成功时置空 |
meta | 可选对象 | 分页响应的元数据:total(总数)、page(页码)、limit(每页条数) |
2.2 为什么需要这层"信封"
- 客户端处理路径统一:无论调用哪个端点,消费方永远先看
success,再决定走data分支还是error分支,不需要为每个接口记忆一套专属的失败返回结构。 - 成功与失败互斥:
data在失败时缺省、error在成功时缺省,杜绝"既返回业务数据又返回错误串"的歧义状态。 - 分页元数据固化:凡是列表接口,分页信息不再散落在响应体的任意角落,而是统一收进
meta,前端可直接透传给分页组件。
2.3 落地增强:让信封具备运行时收敛能力
仅有接口声明还不够——类型会在编译期消失,因此"调用方如何安全地取出 data"才是实战中的关键。可以用类型守卫把success收窄为可判别联合(discriminated union),让成功/失败分支在类型层面彼此独立:
// 升级为可判别联合:success 作为 discriminant type ApiResult<T> = | { success: true; data: T; meta?: { total: number; page: number; limit: number } } | { success: false; error: string } // 类型守卫:一旦返回 true,TS 即把 result 收窄为成功分支 function isSuccess<T>(result: ApiResult<T>): result is Extract<ApiResult<T>, { success: true }> { return result.success } // 消费示例:无需任何 as 断言 const result = await apiFetch<User[]>('/users?page=1&limit=20') if (isSuccess(result)) { // result.data 在此分支中已被确认存在 renderTable(result.data) } else { showToast(result.error) }通用错误处理规范见 rules/typescript/coding-style.md 中"Error Handling"一节——它要求async/await配合try-catch,并对unknown错误先做安全收窄再使用,与上述error字段的收窄逻辑是同一思路的延续。
三、Custom Hooks 模式:把防抖逻辑封装成可复用 React Hook
3.1 规则原文中的 useDebounce
React 组件中大量出现"输入框防抖、窗口尺寸节流、订阅清理"等副作用逻辑。patterns.md 给出的约定样例是useDebounce:
export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value) useEffect(() => { const handler = setTimeout(() => setDebouncedValue(value), delay) return () => clearTimeout(handler) }, [value, delay]) return debouncedValue }3.2 逐行拆解其工程要点
- 泛型
T保留类型:useDebounce<T>(value: T, delay: number): T让 Hook 对字符串、对象、数组等任意输入值通用,且返回值与输入值类型强一致,不会因防抖丢失类型信息。 useState初始化即当前值:首帧渲染直接返回传入的value,保证页面不因防抖出现空白闪烁。useEffect内定时器 + 清理函数成对出现:每次value或delay变化,旧定时器先被clearTimeout清理,再注册新定时器;组件卸载时同样触发清理,杜绝内存泄漏与"卸载后 setState"告警。- 依赖数组
[value, delay]精确声明:只要这两个输入之一变化就重建定时器,是最小且充分的依赖集合。 - 副作用函数必须返回清理函数——这是自定义 Hook 与
useEffect配合时的铁律。
3.3 典型使用场景:搜索请求防抖
function SearchBox() { const [keyword, setKeyword] = useState('') const debouncedKeyword = useDebounce(keyword, 300) useEffect(() => { if (!debouncedKeyword.trim()) return fetch(`/api/search?q=${encodeURIComponent(debouncedKeyword)}`) .then(res => res.json()) .then(data => setResults(data)) }, [debouncedKeyword]) return <input value={keyword} onChange={e => setKeyword(e.target.value)} /> }这样,用户每敲击一次键盘只触发setKeyword,而真正的搜索请求被延迟到连续输入停顿 300ms 后才发起。规则文档本身只保留最小骨架,实际项目还可以在其上继续组合出useDebouncedCallback、useThrottle、usePrevious等变体——但骨架必须遵循"命名以use开头、内部使用 React Hooks、清理副作用"这三大原则,否则无法通过 rules/typescript/testing.md 与 rules/typescript/hooks.md 的约束。
四、Repository 模式:为数据访问套上一层抽象接口
4.1 规则原文中的仓储契约
interface Repository<T> { findAll(filters?: Filters): Promise<T[]> findById(id: string): Promise<T | null> create(data: CreateDto): Promise<T> update(id: string, data: UpdateDto): Promise<T> delete(id: string): Promise<void> }注意这里的方法名与 common/patterns.md 完全对应:findAll、findById、create、update、delete。语言规则负责把通用规则中的这五个标准操作翻译成带类型签名的异步方法:全部返回Promise,符合现代 Node/前端对异步 IO 的假设;findById用T | null显式表达"可能查无此人",而不是抛异常或返回undefined。
4.2 分层语义
Repository<T>只负责"数据访问的形态约定",不包含任何存储实现细节。业务代码面向接口编程,底层实现可以自由切换为数据库、HTTP API 或纯内存存储:
// 内存实现:适合测试与原型 class InMemoryUserRepository implements Repository<User> { private store = new Map<string, User>() async findAll(filters?: Filters): Promise<User[]> { return [...this.store.values()].filter(matchFilters(filters)) } async findById(id: string): Promise<User | null> { return this.store.get(id) ?? null } async create(data: CreateUserDto): Promise<User> { const user = { ...data, id: crypto.randomUUID() } this.store.set(user.id, user) return user } async update(id: string, data: UpdateUserDto): Promise<User> { const existing = this.store.get(id) if (!existing) throw new Error(`User ${id} not found`) const updated = { ...existing, ...data } this.store.set(id, updated) return updated } async delete(id: string): Promise<void> { this.store.delete(id) } }4.3 使用 Repository 模式带来的收益
- 可替换性:从 SQL 数据库切换到 REST API 或缓存层时,只需替换实现类,Service 层零改动——这正是 common/patterns.md 中"轻松切换数据源"的落地方式。
- 可测试性:业务测试可以注入内存假实现或 mock,无需启动真实数据库;配合 rules/typescript/testing.md 中关于隔离 IO 的测试约定,单测速度与稳定性显著提升。
- 类型即文档:
CreateDto/UpdateDto/Filters分别约束"创建所需字段、更新允许字段、筛选条件",把仓储对外接口的输入边界收敛到类型层面,接口误用会在编译期就被拦截。
4.4 在 ECC 中的配套约束
仓储模式并非"想怎么实现就怎么实现"。ECC 的 TypeScript 规则组对其施加了更多约束:
- 方法与入参须显式标注类型、避免内部
any(见 rules/typescript/coding-style.md 的 "Avoidany" 小节,外部不可信输入应使用unknown收窄); - 对象更新使用展开运算符保持不可变(coding-style.md 的 "Immutability" 小节),上面内存实现中的
{ ...existing, ...data }即此规范; - 生产代码不得出现裸
console.log,应改用正式日志库。
五、源码级佐证:ECC 如何自己落实"模式即约束"
ECC 不仅把这三类模式写成规则,更在自己仓库中用工具链把它们变成可自动执行的检查。以下是可在仓库中直接核验的实现证据。
5.1 console.log 自动审计 Hook
coding-style.md 规定 "Noconsole.logstatements in production code",patterns.md 所属的规则组(rules/typescript/hooks.md)则要求通过 Hook 自动提醒。仓库 scripts/hooks/check-console-log.js 正是该检查的执行器:
- 它通过
getGitModifiedFiles(['\\.tsx?$', '\\.jsx?$'])只扫描被修改的 TS/JS 文件; - 通过
EXCLUDED_PATTERNS白名单跳过.test.*、.spec.*、.config.*、scripts/、__tests__/、__mocks__/等允许打印日志的路径; - 一旦发现命中
console.log,即输出WARNING: console.log found in <file>并提示在提交前移除。
这个 Hook 是"规则文档 → 自动化检查"闭环的直接例证:文档定义模式,脚本保障执行,两者一起构成 Agent 与人工评审共同遵守的规范底座。
5.2 规则文件间的交叉引用
- patterns.md 声明"扩展自 common/patterns.md",因此仓储五方法与响应信封语义的"为什么"应查阅 common/patterns.md;
- 模式实现时的"怎么写更安全"应查阅同组 coding-style.md(类型标注、不可变性、
unknown收窄、Zod 输入校验); - 代码落地后的"自动防线"应查阅 hooks.md(Prettier 自动格式化、
tsc类型检查、console.log 告警)与 testing.md。
5.3 查阅与扩展方式
- 英文原版规则:rules/typescript/patterns.md;
- 本仓库西班牙语文档:docs/es/rules/typescript/patterns.md;
- 通用模式基准:docs/es/rules/common/patterns.md;
- 其余 TS 语言规则(同一
paths作用域):coding-style.md、hooks.md、security.md、testing.md; - 自动化执行示例:scripts/hooks/check-console-log.js。
六、三模式协同使用:一套可落地的 TS/JS 工程范式小结
| 模式 | 解决的核心问题 | 最小骨架 | 仓库内配套规则 |
|---|---|---|---|
| API 响应格式 | 所有接口返回结构不统一,客户端处理路径发散 | ApiResponse<T>(success/data/error/meta) | common 通用信封约定 + coding-style 的 unknown 收窄 |
| Custom Hooks | 副作用与重复交互逻辑散落组件内,难以测试复用 | useDebounce<T>:state + effect + 清理函数 | testing.md 的 Hook 测试与 hooks.md 的自动检查 |
| Repository | 业务逻辑与存储细节耦合,数据源难以替换 | Repository<T>五方法异步契约 | common 仓储原则 + coding-style 的类型/不可变约束 |
三者共同回答了一个工程问题:面对一个会持续演进、且由 Agent 大规模参与生成/评审的 TS/JS 代码库,如何让"返回结构、副作用逻辑、数据访问"这三类最高频代码保持形态一致、边界清晰、可测试、可替换。使用这套规则时,建议按以下顺序落地:
- 先让所有 API 层返回统一信封(配合类型守卫消费);
- 把所有跨组件复用的副作用收敛为自定义 Hook(从
useDebounce这类最小原语起步); - 将持久化/远程数据访问封装进仓储接口,业务层只依赖抽象;
- 最后开启同目录 hooks.md 约定的 Prettier +
tsc+ console.log 审计,让机器守住这些模式不被日常提交侵蚀。
这样,"文档里的模式"才能变成"代码库里的现实",而这正是 ECC 作为 Agent 工程规范系统的核心设计意图。
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考