关于 limit-any-type
这项 TypeScript 技能说明随意使用 `any` 的风险,并介绍 `unknown` 和具体类型等替代方案。它面向补充类型、迁移 JavaScript 代码,以及处理库或 API 数据的开发者。
主要功能
- 01说明 `any` 的风险
- 02用 `unknown` 表示未知值
- 03建议定义具体接口与类型别名
- 04识别常见的绕过类型检查做法
- 05指导 API 数据的类型设计
适用场景
- 01替换为消除报错而使用的 `any`
- 02为迁移中的 JavaScript 添加类型
- 03描述来自 API 或库的数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill limit-any-type安装后在 Claude Code 中输入/limit-any-type确认已加载。
详细介绍
限制 any 类型的使用
概述
any 类型实际上会禁用 TypeScript 对使用它的代码进行类型检查。
虽然 any 可以作为应急手段,但它会消除 TypeScript 的优势:类型安全、开发体验、重构信心和预防 Bug。
何时使用此技能
- 想用 any 来消除类型错误
- 将 JavaScript 代码迁移到 TypeScript
- 使用没有类型定义的第三方库
- 因复杂的类型错误而感到沮丧
- 为现有代码添加类型注解
铁律
NEVER use any without a specific, documented reason.
没有例外:
- 不能因为“只是快速修复一下”
- 不能因为“类型太复杂了”
- 不能因为“以后再修”
- 不能因为“运行时能正常工作”
检测:“any 异味”
如果你要写 any,先停下来问:“有没有更好的办法?”
// ❌ VIOLATION: Using any because it's "easy"
function processData(data: any) {
return data.items.map((item: any) => item.name);
}
// ✅ CORRECT: Define proper types
interface DataItem {
name: string;
}
interface Data {
items: DataItem[];
}
function processData(data: Data) {
return data.items.map(item => item.name);
}
any 的五大危害
1. 没有类型安全
let age: number;
age = '12' as any; // No error, but wrong!
age += 1; // "121" at runtime
2. 破坏约定
function calculateAge(birthDate: Date): number { /* ... */ }
let birthDate: any = '1990-01-19'; // string, not Date!
calculateAge(birthDate); // No error, but will fail
3. 缺少语言服务
// With proper types: autocomplete, refactoring, documentation
person. // Shows: name, age, email...
// With any: nothing
(person as any). // No autocomplete
4. 掩盖重构 Bug
interface Props {
onSelectItem: (id: number) => void; // Changed from (item: any)
}
function handleSelectItem(item: any) {
selectedId = item.id; // Bug! Should be just `item` now
}
5. 隐藏类型设计问题
// ❌ BAD: What is this state?
const appState: any = { /* ... */ };
// ✅ GOOD: Clear, documented design
interface AppState {
user: User | null;
preferences: UserPreferences;
isLoading: boolean;
}
抵御压力的原则
1. “太复杂了”
压力:“这个类型太复杂了,用 any 更简单。”
**回应:**复杂的类型往往意味着复杂的数据。定义类型会迫使你理解并记录数据模型。
**行动:**拆分类型。使用接口和类型别名。问问自己:“这些数据实际是什么样的?”
2. “以后再修”
压力:“我之后会回来补上合适的类型。”
**回应:**你不会的。技术债务会不断累积。any 会在代码库中蔓延。
**行动:**现在就修复。如果确实做不到,就添加带有工单号的 // TODO。
3. “这个库没有类型定义”
压力:“这个 npm 包没有 @types。”
**回应:**你有其他选择:编写最精简的类型、使用 unknown,或者创建 .d.ts 文件。
**行动:**对于你无法控制的值,使用 unknown。针对实际用到的内容创建专用接口。
any 的更好替代方案
| 不要使用 | 改用 |
|---|---|
用于未知值的 any | unknown(强制你先收窄类型) |
any[] | unknown[] 或具体类型 |
(x: any) => any | 正确的函数签名 |
Record<string, any> | Record<string, unknown> |
用于 JSON 的 any | 使用 unknown 解析并验证 |
危险信号——停下来重新考虑
- 在同一个函数中使用多个
any类型 - 用
as any消除错误 - 使用
// @ts-ignore或@ts-expect-error绕过检查 - 在公开 API 签名中使用 any
- 在函数返回类型中使用 any
常见的自我开脱(都不成立)
| 借口 | 事实 |
|---|---|
| “运行时能正常工作” | 类型的作用是在运行前捕获 Bug。 |
| “TypeScript 太严格了” | TypeScript 正在保护你。弄清楚它在告诉你什么。 |
| “我不知道类型是什么” | 使用 unknown 并收窄类型。 |
| “这是第三方库” | 为你实际用到的内容编写最精简的类型。 |
| “这只是内部代码” | 内部代码也是代码。以后你还是得调试它。 |
快速参考
| 症状 | 行动 |
|---|---|
| 不理解的类型错误 | 仔细阅读错误信息,使用 unknown |
| 复杂的嵌套类型 | 拆分成更小的接口 |
| 来自 API 的动态数据 | 定义响应类型,在边界处进行验证 |
| 从 JS 迁移 | 从 unknown 开始,逐步添加类型 |
归根结底
每个 any 都是一个随时可能发生的 Bug。
对不了解的值使用 unknown。对已知的值使用合适的类型。必须使用 any 时,将其限制在尽可能小的范围内,说明原因,并计划将其移除。
参考资料
改编自 Dan Vanderkam 的《Effective TypeScript》,第 5 条:限制 any 类型的使用。