---
description: 工具函数规范 - 纯函数设计、分类策略、类型定义。
globs: ["**/utils/**/*.ts", "**/helpers/**/*.ts"]
alwaysApply: false
---

# 工具函数规范

该规则用于工具函数开发：

**当创建新工具函数时：**

- 优先纯函数，副作用函数用 `@sideEffect` 标注
- 按功能分类：`format` / `validate` / `storage` / `dom`

**当区分通用 vs 业务工具时：**

- 通用工具（无业务语义）→ `src/utils/`
- 业务工具（含业务语义）→ `src/utils/biz/`

**当设计函数类型时：**

- 通用数据处理使用泛型
- 多种调用方式使用重载

---

## 📁 文件组织

**方案 A：按功能分文件**（推荐）

```
{utils目录}/
├── format.ts     # 格式化
├── validate.ts   # 验证
├── storage.ts    # 存储
├── request.ts    # 请求
└── index.ts      # 统一导出
```

**方案 B：按领域分目录**（大型项目）

```
{utils目录}/
├── format/
│   ├── date.ts
│   └── index.ts
├── validate/
│   └── index.ts
└── index.ts
```

> 📋 具体存放路径详见 @foundation/project.mdc

---

## 🎨 设计策略

### 1. 分类策略

**功能分类**：

| 分类      | 文件名          | 内容示例                         |
| --------- | --------------- | -------------------------------- |
| 格式化    | `format.ts`     | `formatDate`、`formatMoney`      |
| 验证      | `validate.ts`   | `isEmail`、`isPhone`             |
| 存储      | `storage.ts`    | `localStorage` 封装              |
| DOM       | `dom.ts`        | `scrollTo`、`getViewportSize`    |
| 数组/对象 | `collection.ts` | `groupBy`、`unique`、`deepClone` |

**通用 vs 业务工具**：

| 类型     | 放置位置         | 特点                     | 示例                    |
| -------- | ---------------- | ------------------------ | ----------------------- |
| 通用工具 | `src/utils/`     | 无业务语义，可跨项目复用 | `formatDate`、`isEmail` |
| 业务工具 | `src/utils/biz/` | 含业务语义，项目特有     | `formatOrderStatus`     |

### 2. 纯函数策略

**原则**：优先使用纯函数，副作用函数明确标注

**判断标准**：

| 特征               | 纯函数 | 副作用函数 |
| ------------------ | ------ | ---------- |
| 相同输入相同输出   | ✅     | ❌         |
| 不修改外部状态     | ✅     | ❌         |
| 不依赖外部可变状态 | ✅     | ❌         |
| 无 I/O 操作        | ✅     | ❌         |

```typescript
// ✅ 纯函数
export function formatMoney(amount: number): string {
  return `¥${amount.toFixed(2)}`;
}

// ⚠️ 副作用函数：需 @sideEffect 标注
/** @sideEffect 写入 localStorage */
export function saveToStorage(key: string, value: unknown): void {
  localStorage.setItem(key, JSON.stringify(value));
}
```

### 3. 类型设计

**泛型使用**：

```typescript
// 通用数据处理函数使用泛型
export function groupBy<T, K extends string | number>(
  arr: T[],
  keyFn: (item: T) => K
): Record<K, T[]> {
  return arr.reduce(
    (acc, item) => {
      const key = keyFn(item);
      (acc[key] ||= []).push(item);
      return acc;
    },
    {} as Record<K, T[]>
  );
}
```

**函数重载**：

```typescript
// 多种调用方式使用重载
export function formatDate(date: Date): string;
export function formatDate(date: Date, format: string): string;
export function formatDate(timestamp: number): string;
export function formatDate(
  input: Date | number,
  format = "YYYY-MM-DD"
): string {
  const date = typeof input === "number" ? new Date(input) : input;
  // 实现...
  return "";
}
```

---

## ✅ 检查清单

### 设计

- ✅ 工具函数必须是纯函数，或明确标注副作用
- ✅ 函数必须有明确的输入输出类型
- ✅ 每个工具函数必须单一职责
- ✅ 工具函数必须可独立测试

### 禁止

- ❌ 禁止工具函数依赖组件或页面
- ❌ 禁止工具函数访问全局状态（Store）
- ❌ 禁止工具函数产生不可预期的副作用
- ❌ 禁止将业务逻辑放在通用工具函数中

> 📋 TypeScript 规范详见 @foundation/typescript.mdc

---

## 📋 标准模板

### 格式化工具

```typescript
// utils/format.ts

/** 格式化金额 */
export function formatMoney(amount: number, prefix = "¥"): string {
  return `${prefix}${amount.toFixed(2)}`;
}

/** 格式化手机号（隐藏中间 4 位） */
export function formatPhone(phone: string): string {
  return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}

/** 格式化文件大小 */
export function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
}
```

### 泛型工具

```typescript
// utils/collection.ts

/** 数组去重 */
export function unique<T>(arr: T[]): T[] {
  return [...new Set(arr)];
}

/** 数组分组 */
export function groupBy<T, K extends string | number>(
  arr: T[],
  keyFn: (item: T) => K
): Record<K, T[]> {
  return arr.reduce(
    (acc, item) => {
      const key = keyFn(item);
      (acc[key] ||= []).push(item);
      return acc;
    },
    {} as Record<K, T[]>
  );
}

/** 深拷贝 */
export function deepClone<T>(obj: T): T {
  return JSON.parse(JSON.stringify(obj));
}
```
