---
description: 注释规范 - 注释时机、JSDoc 格式、TODO 规范。涉及编写或审查注释时触发。
---

# 注释规范

该规则定义注释编写标准，核心原则：代码优先自解释，注释仅解释"为什么"。

---

## 必须遵守

- ✅ 注释解释"为什么"，而非"做什么"
- ✅ 公共 API、复杂算法、业务规则必须有注释
- ✅ 注释必须与代码同步更新
- ✅ 导出函数必须有 JSDoc（功能、参数、返回值）
- ✅ 复杂公共接口提供 `@example`
- ✅ 类型定义关键字段必须有注释
- ✅ Hack/Workaround 必须注释原因
- ✅ TODO/FIXME 必须包含责任人和期限
- ✅ 魔法数字用常量替代并注释含义
- ✅ 中文项目使用中文注释

---

## 严格禁止

- ❌ 注释显而易见的代码
- ❌ 用注释弥补糟糕的命名
- ❌ 大段注释掉的代码
- ❌ 自动生成的无用注释
- ❌ 过期或与代码不符的注释
- ❌ 无意义的 TODO（如"待完善"无具体说明）
- ❌ 行尾超过 80 字符的注释
- ❌ 简单 getter/setter 的注释

---

## 最佳实践

- 💡 代码清晰时无需注释
- 💡 良好命名的函数无需额外注释
- 💡 简单变量赋值无需注释
- 💡 注释优先级：清晰代码 > JSDoc > 文档文件

---

## 代码示例

### 注释时机

```typescript
// ✅ 需要注释：业务规则
function calculateShippingFee(order: Order): number {
  // 订单金额超过 99 元免运费（2024 促销规则）
  if (order.totalAmount >= 99) return 0;

  // 偏远地区运费加倍
  const remoteRegions = ["西藏", "新疆", "内蒙古"];
  const isRemote = remoteRegions.includes(order.shippingAddress.province);
  return isRemote ? 20 : 10;
}

// ✅ 需要注释：Hack/临时方案
// HACK: 兼容 iOS 12 不支持 IntersectionObserver
// TODO(@user, 2024-12-31): 届时移除此 polyfill
if (!window.IntersectionObserver) {
  await import("intersection-observer");
}

// ❌ 不需要注释：代码自解释
const activeUsers = users.filter((user) => user.isActive);

// ❌ 不需要注释：重复函数名
// 验证邮箱是否有效
function isValidEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
```

### JSDoc 函数注释

```typescript
/**
 * 发送验证码
 * @param email - 邮箱地址
 * @param type - 验证码类型
 * @throws {InvalidEmailError} 邮箱格式错误
 * @throws {RateLimitError} 60秒内只能发送一次
 * @example await sendVerificationCode('user@example.com', 'login')
 */
export async function sendVerificationCode(
  email: string,
  type: "login" | "register" | "reset"
): Promise<boolean> {
  // ...
}
```

### TODO/FIXME 规范

```typescript
// ✅ 完整信息
// TODO(@username, 2024-12-31): 优化性能，使用虚拟滚动
// FIXME(@username, 2024-12-20): 修复 Safari 兼容性（Issue #123）

// ❌ 无用信息
// TODO: 待完善
// FIXME: 有问题
```

### 复杂算法注释

```typescript
/**
 * 二分查找
 * 时间复杂度：O(log n)
 * @param arr - 有序数组（升序）
 * @param target - 目标值
 * @returns 索引，未找到返回 -1
 */
export function binarySearch(arr: number[], target: number): number {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = left + ((right - left) >> 1); // 避免大数溢出
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1;
}
```
