Topic | 代码风格与 React Compiler 集成¶
重要性:⭐⭐⭐(读懂 Claude Code 源码的"语法"前提) 出现位置:几乎所有
.tsx顶部 关联:glossary 的 React Compiler 词条
1. React Compiler 痕迹¶
Claude Code 大量使用 React Compiler(React 19+ 的优化编译器)。每个 .tsx 文件顶部都有:
这是 React Compiler 编译后的产物:
- 源文件用 React 19 风格写(不需要 useMemo / useCallback)
- 编译器自动分析依赖,插入 memoization
- 编译后生成 _c(N) 引用,N 是 memo cache 槽位
示例(编译前 vs 编译后):
// 编译前(你写的)
function Dialog({ title, onCancel, children }) {
return (
<Box flexDirection="column">
<Text>{title}</Text>
{children}
</Box>
)
}
// 编译后(React Compiler 生成)
import { c as _c } from "react/compiler-runtime"
function Dialog(t0) {
const $ = _c(27); // 27 = memo cache 槽位数
// ... 编译器分析 props 依赖,生成缓存检查代码
return ...
}
关键:
- t0 是 props 形参(编译器重命名了)
- _c(27) 是 memoization 入口
- 源代码里看起来怪,是因为这是编译产物
2. import / 命名习惯¶
2.1 显式 .js 后缀(即使源是 .ts)¶
原因:
- Claude Code 用 Bun 运行时 + ESM
- ESM 规范要求显式扩展名
- 配合 TypeScript 的 moduleResolution: "bundler" 工作
- 即使源是 bar.ts,import 写 bar.js(TS 解析时映射到 .ts)
好处:
- 跨运行时一致(Node ESM、Bun、Deno)
- TypeScript 配置 allowImportingTsExtensions 不会出错
2.2 biome / biome-ignore 注释¶
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
import { feature } from 'bun:bundle'
import { readFileSync } from 'fs'
// ...
含义:
- 项目用 biome(不是 ESLint + Prettier)
- biome 默认会自动重排 import
- 但 Claude Code 的 ANT-ONLY 标记有顺序要求(require 必须在 import 后)
- // biome-ignore 禁用这一行规则
实战:
- 看到 // biome-ignore 就知道这块代码有特殊考虑
- 不要"按 biome 提示"自动修复
2.3 ESLint 自定义规则¶
Claude Code 用自定义 ESLint 规则:
- custom-rules/no-top-level-side-effects —— 禁止顶层副作用
- custom-rules/no-process-env-top-level —— 禁止顶层读 env
- custom-rules/safe-env-boolean-check —— 安全布尔检查
意义:
- 项目有严格的代码规范
- 不是个人喜好,是深思熟虑的工程决策
- 例:no-top-level-side-effects 是因为 import 顺序会影响启动行为
3. JSDoc 注释风格¶
/**
* True when a task is in a terminal state and will not transition further.
* Used to guard against injecting messages into dead teammates, evicting
* finished tasks from AppState, and orphan-cleanup paths.
*/
export function isTerminalTaskStatus(status: TaskStatus): boolean {
return status === 'completed' || status === 'failed' || status === 'killed'
}
特点: - 注释解释为什么(不是什么) - 列出使用场景 - 三段式:summary + use cases + implementation
4. TypeScript 风格¶
4.1 type vs interface¶
// Claude Code 几乎全部用 type,不用 interface
export type Tool = { name: string, description: string, ... }
export type Message = UserMessage | AssistantMessage | ...
export type AppState = { ... }
为什么:
- type 支持 union(A | B)
- type 支持 intersection(A & B)
- type 支持 mapped type
- interface 不能做 union(只能 extends)
4.2 Discriminated Union¶
export type Message =
| { type: 'user', text: string }
| { type: 'assistant', text: string, toolUses: ToolUse[] }
| { type: 'tool_use', tool: string, input: unknown }
| { type: 'tool_result', result: ToolResult }
配套类型守卫:
function isUserMessage(m: Message): m is Extract<Message, { type: 'user' }> {
return m.type === 'user'
}
4.3 DeepImmutable¶
export type DeepImmutable<T> = {
readonly [K in keyof T]: DeepImmutable<T[K]>
}
export type ToolPermissionContext = DeepImmutable<{
allowedTools: Set<string>
deniedTools: Set<string>
// ...
}>
意义: - 整个对象递归 readonly - TypeScript 防止意外 mutation - 运行时不实际freeze(性能考虑)
5. 函数式风格 vs OOP¶
5.1 大多数是函数¶
export function findToolByName(tools: Tools, name: string): Tool | undefined {
return tools.find(tool => toolMatchesName(tool, name))
}
5.2 少数用 class¶
export class QueryEngine {
private retryCount = 0
private totalCost = 0
async *ask(messages: Message[]): AsyncGenerator<AskEvent> { ... }
}
什么时候用 class: - 跨调用的状态(retryCount 在多次 ask 之间累加) - DI 容器(QueryEngine 接受一堆依赖) - 极少数场景
什么时候用函数: - 纯计算 - 一次性操作 - 工具函数
哲学:默认函数,需要状态用 class。
6. 错误处理¶
6.1 三种风格¶
// 风格 1:try/catch + 转换
try {
return await someAsyncOp()
} catch (err) {
throw toError(err) // 转成标准 Error
}
// 风格 2:Result 类型
type Result<T> = { ok: true, value: T } | { ok: false, error: Error }
// 风格 3:自定义 Error 子类
export class FallbackTriggeredError extends Error {
constructor(public originalError: Error) {
super(`Fallback triggered: ${originalError.message}`)
}
}
6.2 错误命名¶
*Error后缀(不是*Exception)- 描述性名字(
RateLimitError、AuthenticationError)
7. 关键洞察¶
7.1 React Compiler 是"未来的 React"¶
Claude Code 是早期采用 React Compiler 的项目。读源码看到的"怪样子"是编译产物,不是手写代码。
7.2 严格 TypeScript 是项目"安全网"¶
- type-only imports
- DeepImmutable
- discriminated union + type guards
- 运行时类型 + 编译时类型双保险
7.3 自定义 ESLint 规则体现"团队规范"¶
no-top-level-side-effects反映"启动顺序敏感"no-process-env-top-level反映"env 读取要可控"- 规则的本质是团队共识的代码化
8. 阅读清单¶
- ✅ 任意
.tsx文件顶部 —— 看 React Compiler 痕迹 - ✅
src/state/store.ts—— 函数式 + 严格类型 - ✅
src/QueryEngine.ts—— 少数 class 用法 - ✅
src/Task.ts—— discriminated union - 📌
.eslintrc/biome.json—— 实际规则定义 - 📌
tsconfig.json—— 严格模式
9. 练习任务¶
- 用 React Compiler 编译你的代码 —— 比较前后差异
- 写一个 DeepImmutable —— 用 mapped type 递归
- 实现 isUserMessage 类型守卫 —— 让 TS 智能收窄类型
- 思考:如果你是 Claude Code 团队负责人,你会加哪些自定义 ESLint 规则?为什么?