关于 narrow-any-scope
面向 TypeScript 开发者的编码技能,说明如何在必须使用 `any` 时限制其影响范围,避免类型安全问题扩散。适用于处理无类型依赖、复杂类型错误和代码审查。
主要功能
- 01缩小赋值中的 `any` 范围
- 02使用内联 `any` 断言
- 03标注函数返回类型
- 04用带类型变量约束值
- 05采用精确的回调签名
适用场景
- 01处理无类型的第三方代码
- 02将 JavaScript 迁移到 TypeScript
- 03审查使用 `any` 的代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill narrow-any-scope安装后在 Claude Code 中输入/narrow-any-scope确认已加载。
详细介绍
尽可能缩小 any 类型的作用域
概述
如果必须使用 any,就要控制影响范围。
any 会传染——它会在代码中扩散。尽可能缩小它的作用域,以限制其对类型安全的影响。
何时使用此技能
- 因某些原因不得不使用 any
- 处理未类型化的第三方代码
- 处理复杂的类型错误
- 将 JavaScript 迁移到 TypeScript
- 审查使用 any 的代码
铁律
NEVER let any escape its minimal necessary scope.
没有例外:
- 不能因为“这只是一个变量”
- 不能因为“我之后会修复”
- 不能因为“这个函数很短”
检测:“any 外溢”的迹象
如果 any 的作用域还能进一步缩小,就缩小它的作用域。
// ❌ VIOLATION: any on parameter escapes to return value
function processExpression(expression: any) {
const result = expression.evaluate(); // result is any
return result; // Returns any - spreads everywhere
}
// ✅ BETTER: any only where needed
function processExpression(expression: any) {
const result: number = expression.evaluate(); // Constrain immediately
return result; // Returns number
}
技巧 1:在赋值时缩小作用域
// ❌ BAD: any spreads
const config = getConfig() as any;
config.debug = true; // config stays any
const debug = config.debug; // debug is any
// ✅ GOOD: Constrain immediately
const config = getConfig() as any;
const debug: boolean = config.debug; // debug is boolean
技巧 2:内联 any 类型断言
// ❌ BAD: any on the whole object
function processObject(obj: any) {
obj.x = 1;
obj.y = 2;
return obj; // Returns any
}
// ✅ GOOD: any only on the problematic line
function processObject(obj: Foo) {
// @ts-expect-error - The library typings are wrong here
(obj as any).secretMethod();
return obj; // Returns Foo
}
技巧 3:返回类型注解
// ❌ BAD: Return type infected by any
function parse(input: string) {
const parsed = JSON.parse(input); // parsed is any
return parsed; // Return type is any
}
// ✅ GOOD: Explicit return type blocks the any
function parse(input: string): ParsedData {
const parsed = JSON.parse(input); // Still any internally
return parsed; // But return type is ParsedData
}
技巧 4:使用中间变量
// ❌ BAD: any infects the whole expression
function calculate(a: number, b: any, c: number) {
return a + b + c; // Result is any because b is any
}
// ✅ BETTER: Convert any before use
function calculate(a: number, b: any, c: number) {
const bNum: number = b; // Constrain b to number
return a + bNum + c; // Result is number
}
技巧 5:使用类型断言,而非 any 参数
// ❌ BAD: Changing parameter type to any
function processUser(user: any) {
return user.name.toUpperCase();
}
// ✅ GOOD: Keep the type, assert where needed
function processUser(user: User) {
// Only use any assertion if absolutely necessary
return (user as any).secretField; // any is inline only
}
any[] 陷阱
// ❌ DANGEROUS: any[] elements are any
function getFirst(arr: any[]) {
return arr[0]; // Returns any
}
// ✅ SAFER: Use generics or unknown
function getFirst<T>(arr: T[]): T {
return arr[0]; // Returns T
}
function getFirst(arr: unknown[]): unknown {
return arr[0]; // Returns unknown - forces checking
}
函数签名:让 any 更精确
// ❌ BAD: Maximally broad any
type Callback = any;
// ✅ BETTER: Precise function types
type Callback0 = () => any; // No params
type Callback1 = (arg: any) => any; // One param
type CallbackN = (...args: any[]) => any; // Any params
抵御压力指南
1. “就这一个地方”
压力:“any 反正是隔离的”
**回应:**any 会通过返回类型、赋值和函数调用扩散。
**行动:**缩小其作用域。立即添加类型约束。
2. “类型太复杂了”
压力:“我搞不清正确的类型”
**回应:**可以暂时使用 any,但要约束输出。
**行动:**添加显式返回类型。添加中间类型注解。
3. “这是内部代码”
压力:“没有外部代码会用到它”
**回应:**未来的你就是外部代码。
**行动:**缩小 any 的作用域。说明使用它的原因。
危险信号——停止并重新考虑
- 函数参数使用 any
- 函数返回 any(尤其是没有显式返回类型时)
- any 被赋给一个会多次使用的变量
- any 在表达式中扩散
- 多个函数之间传递 any
常见的自我辩解(都不成立)
| 借口 | 事实 |
|---|---|
| “它是隔离的” | any 会通过返回和赋值扩散。 |
| “只用一次” | 这就是一个漏洞的开端。 |
| “我之后会修复” | 之后永远不会到来。现在就缩小作用域。 |
| “这个函数很短” | 短函数也可能让 any 扩散。 |
快速参考
| any 的使用场景 | 缩小作用域的技巧 |
|---|---|
| any 参数 | 添加返回类型注解 |
| any 返回值 | 添加显式返回类型 |
| 表达式中的 any | 赋值给有类型的变量 |
| 来自 JSON.parse 的 any | 添加返回类型或进行验证 |
| 来自库的 any | 断言为特定类型 |
审查 any 的用法
// Before: any everywhere
function process(data: any): any {
const x = data.foo; // any
const y = data.bar; // any
return { x, y }; // any
}
// After: any contained
function process(data: any): Result {
const x: number = data.foo; // number
const y: string = data.bar; // string
return { x, y }; // Result
}
核心要点
把 any 当作危险物质处理。控制泄漏。
如果必须使用 any,就将它用于尽可能小的一段代码。添加类型注解,阻止它扩散。目标是除了那一行确实需要 any 的代码外,其他所有地方都保持类型安全。
参考资料
基于 Dan Vanderkam 的《Effective TypeScript》,第 43 条:尽可能缩小 any 类型的作用域。
评论
支持 Markdown