Codex 又造了一个和现有类型重名的 interface:怎么让它先搜再写

Codex 新建了一个 User 或 ApiResponse 类型,其实别处已经有一个一模一样的。如何用 AGENTS.md、共享 types 模块、ts-morph 强制它先搜。

你 review Codex 的 PR,发现它在 src/auth/types.ts 加了个 User interface。三个问题:你 src/types/user.ts 里已经有 User、新这个字段稍有不同、现在一半代码 import 老的、一半 import 新的。TypeScript 不会报错,因为两个结构兼容到能互相赋值。

Codex 写之前没搜。它看了局部 context,识别出需要的形状,就在原地写了一个新声明。没有一个强制函数——显式的 “先搜” 规则、agent 知道去看的共享 types module、或 CI 抓重名声明——每次 refactor 都会重演。

常见原因

1. Agent 创建前没 grep

Codex 默认流程是 “读我要改的文件,补缺的东西”。缺的是个 type,它就写一个 inline。它很少在整个 repo 搜现有定义。

如何判断:PR 加了新的 interface Xtype X = ...grep -rn "interface X\|type X " src/ 显示别处还有一份。

2. Types 散落在 repo 各处,没有中心索引

一半 type 在 src/types/,一半和首个使用者放一起(src/auth/types.tssrc/api/types.ts)。没有明显的 “type 该放哪” 答案。Codex 耸肩,加到当前文件里。

如何判断find src -name 'types.ts' -o -name 'types/*.ts' 返回十个以上 path。没有 src/types/index.ts 在 re-export。

3. AGENTS.md 没要求搜

Agent 指引里没写 type 复用规则。Codex 选局部最方便的路径。

如何判断grep -i 'type\|interface\|reuse' AGENTS.md 没结果。

4. 现有 type 在 barrel 里,agent 没加载

你有 src/types/index.ts 在 re-export 所有 type,但 agent 只 open 了它要改的那个文件。barrel 没见到,就当作没有。

如何判断:transcript 只对当前文件 read_file。没有跨 repo grep / search 找过 type 名。

5. Type 名和常见库重名

Codex 看到 Response 以为是 DOM 的 Response。它新建了 ApiResponse,根本没查你是不是已经有了。常见名(UserItemResponseErrorConfig)最容易撞。

如何判断:diff 加的 type 是泛名。已有那个也是同样泛名。

最短修复路径

Step 1:AGENTS.md 加 “先搜再建” 规则

## Type 和 interface 复用

新建 `type``interface``class``enum` 之前:

1.`grep -rn "interface <Name>\b\|type <Name>\b\|class <Name>\b" src/`
2. 已有定义就 import,不要重复声明。
3. 已有那个缺你需要的字段,用 `interface X extends BaseX` 扩展,
   或就地更新——不要分叉。
4. 新 type 放 `src/types/<domain>.ts`。除非 type 是单模块私有,
   不要 inline 在 feature 文件里。

名字泛的时候(`User``Item``Response`)搜广一点——`packages/`
`apps/``shared/` 都搜。

规则是 agent 真能跟着做的:搜命令 + 决策树。

Step 2:把 type 集中到单一 import path

src/types/index.ts 做成 barrel,re-export 所有共享 type:

// src/types/index.ts
export type { User } from './user'
export type { Session } from './session'
export type { ApiResponse, ApiError } from './api'
export type { Permission } from './permission'

然后 AGENTS.md 里:

所有共享 type 从 `@/types` 出口:

    import type { User } from '@/types'

`@/types` 里没有的,就是 module 私有。任何可能被复用的 type 在新建之前都先搜 `@/types`

单一 import path 让 “之前有没有” 一个 grep 就能答。

Step 3:ts-morph CI 抓重名 type

// scripts/check-duplicate-types.ts
import { Project } from 'ts-morph'

const project = new Project({ tsConfigFilePath: 'tsconfig.json' })
const decls = new Map<string, string[]>()

for (const file of project.getSourceFiles('src/**/*.ts')) {
  for (const iface of file.getInterfaces()) {
    const name = iface.getName()
    const list = decls.get(name) ?? []
    list.push(file.getFilePath())
    decls.set(name, list)
  }
  for (const alias of file.getTypeAliases()) {
    const name = alias.getName()
    const list = decls.get(name) ?? []
    list.push(file.getFilePath())
    decls.set(name, list)
  }
}

const dupes = [...decls.entries()].filter(([, files]) => files.length > 1)
if (dupes.length > 0) {
  for (const [name, files] of dupes) {
    console.error(`Duplicate type ${name}:`)
    for (const f of files) console.error(`  ${f}`)
  }
  process.exit(1)
}

接到 CI 当 required check。Codex 看到红 CI 会自己修。

Step 4:在 lint 错误里指向规范位置

codebase 有自定义 ESLint rule 的话,加一个 no-redeclare-shared-type 指到规范路径。没有自定义 rule 也能用 eslint-plugin-importno-duplicatesno-restricted-imports,把 import 都收口到 @/types

{
  "rules": {
    "no-restricted-imports": ["error", {
      "patterns": [{
        "group": ["**/auth/types", "**/api/types", "**/user/types"],
        "message": "共享 type 从 @/types import,不要走深路径。"
      }]
    }]
  }
}

Codex 读得到 ESLint 输出,自己会改。

Step 5:agent PR 先看 import

PR checklist:

- [ ] 没有新增 `interface``type``@/types` 里重名
- [ ] 新 type 都在 `src/types/<domain>.ts`,不在 feature 文件里 inline
- [ ] `npm run check:types` 过(跑 ts-morph 的重名检查)

这是 ts-morph 抓不到的情况的人工兜底(同名但形状稍不同)。

预防

  • AGENTS.md 强制 “先 grep 再建”,连具体 grep 命令一起给
  • 所有共享 type 放 src/types/,从 src/types/index.ts 出口
  • ts-morph CI 检查重名 interface/type
  • ESLint no-restricted-imports 把 import 收到 @/types
  • Reviewer 抽查 agent PR 里新加的 interface / type 声明
  • 泛名(UserItemResponse)强制加更长的前缀避免未来撞名

相关

标签: #Codex #agent #排查 #types