narrow-any-scope
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUse the Narrowest Possible Scope for any Types
尽可能缩小any类型的作用域
Overview
概述
If you must use any, contain the damage.
any is contagious - it spreads through your code. Keep it as narrowly scoped as possible to limit its impact on type safety.
如果必须使用any类型,请控制其影响范围。
any类型具有传染性——它会在你的代码中扩散。请将其作用域尽可能缩小,以限制它对类型安全性的影响。
When to Use This Skill
何时使用此技巧
- Forced to use any for some reason
- Working with untyped third-party code
- Dealing with complex type errors
- Migrating JavaScript to TypeScript
- Reviewing code that uses any
- 因某些原因被迫使用any类型
- 处理无类型的第三方代码
- 解决复杂的类型错误
- 将JavaScript代码迁移至TypeScript
- 审查使用了any类型的代码
The Iron Rule
铁则
NEVER let any escape its minimal necessary scope.No exceptions:
- Not for "it's just one variable"
- Not for "I'll fix it later"
- Not for "the function is short"
NEVER let any escape its minimal necessary scope.无例外:
- 不要以“只是一个变量”为借口
- 不要以“我之后再修复”为借口
- 不要以“函数很短”为借口
Detection: The "Escaping any" Smell
检测:“any类型逃逸”的迹象
If any could be more narrowly scoped, scope it.
typescript
// ❌ 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
}如果any类型的作用域可以进一步缩小,就缩小它。
typescript
// ❌ 违规:参数上的any类型扩散至返回值
function processExpression(expression: any) {
const result = expression.evaluate(); // result是any类型
return result; // 返回any类型——会扩散到各处
}
// ✅ 更佳:仅在必要处使用any类型
function processExpression(expression: any) {
const result: number = expression.evaluate(); // 立即约束类型
return result; // 返回number类型
}Technique 1: Narrow in Assignments
技巧1:在赋值时缩小作用域
typescript
// ❌ 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 booleantypescript
// ❌ 不良:any类型扩散
const config = getConfig() as any;
config.debug = true; // config仍为any类型
const debug = config.debug; // debug是any类型
// ✅ 良好:立即约束类型
const config = getConfig() as any;
const debug: boolean = config.debug; // debug是boolean类型Technique 2: Inline any Assertions
技巧2:内联any类型断言
typescript
// ❌ 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
}typescript
// ❌ 不良:整个对象使用any类型
function processObject(obj: any) {
obj.x = 1;
obj.y = 2;
return obj; // 返回any类型
}
// ✅ 良好:仅在有问题的行使用any类型
function processObject(obj: Foo) {
// @ts-expect-error - 该库的类型定义有误
(obj as any).secretMethod();
return obj; // 返回Foo类型
}Technique 3: Return Type Annotations
技巧3:添加返回类型注解
typescript
// ❌ 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
}typescript
// ❌ 不良:返回值被any类型污染
function parse(input: string) {
const parsed = JSON.parse(input); // parsed是any类型
return parsed; // 返回类型为any
}
// ✅ 良好:显式返回类型阻止any类型扩散
function parse(input: string): ParsedData {
const parsed = JSON.parse(input); // 内部仍为any类型
return parsed; // 但返回类型为ParsedData
}Technique 4: Intermediate Variables
技巧4:使用中间变量
typescript
// ❌ 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
}typescript
// ❌ 不良:any类型污染整个表达式
function calculate(a: number, b: any, c: number) {
return a + b + c; // 结果为any类型,因为b是any
}
// ✅ 更佳:使用前转换any类型
function calculate(a: number, b: any, c: number) {
const bNum: number = b; // 将b约束为number类型
return a + bNum + c; // 结果为number类型
}Technique 5: Type Assertions Over any Parameters
技巧5:对any参数使用类型断言
typescript
// ❌ 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
}typescript
// ❌ 不良:将参数类型改为any
function processUser(user: any) {
return user.name.toUpperCase();
}
// ✅ 良好:保留原有类型,仅在必要处断言
function processUser(user: User) {
// 仅在绝对必要时使用any断言
return (user as any).secretField; // any仅在该行内联使用
}The any[] Trap
any[]的陷阱
typescript
// ❌ 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
}typescript
// ❌ 危险:any[]的元素都是any类型
function getFirst(arr: any[]) {
return arr[0]; // 返回any类型
}
// ✅ 更安全:使用泛型或unknown
function getFirst<T>(arr: T[]): T {
return arr[0]; // 返回T类型
}
function getFirst(arr: unknown[]): unknown {
return arr[0]; // 返回unknown类型——强制进行类型检查
}Function Signatures: More Precise any
函数签名:更精准的any类型
typescript
// ❌ 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 paramstypescript
// ❌ 不良:范围过宽的any类型
type Callback = any;
// ✅ 更佳:精准的函数类型
type Callback0 = () => any; // 无参数
type Callback1 = (arg: any) => any; // 一个参数
type CallbackN = (...args: any[]) => any; // 任意数量参数Pressure Resistance Protocol
抗压应对方案
1. "It's Just This One Place"
1. “只是这一处而已”
Pressure: "The any is isolated anyway"
Response: any spreads through return types, assignments, and function calls.
Action: Scope it narrower. Add type constraints immediately.
压力:“any类型已经被隔离了”
**回应:**any类型会通过返回值、赋值和函数调用扩散。
**行动:**进一步缩小其作用域。立即添加类型约束。
2. "The Type Is Too Complex"
2. “类型太复杂了”
Pressure: "I can't figure out the right type"
Response: Use any temporarily, but constrain the output.
Action: Add explicit return type. Add intermediate type annotations.
压力:“我搞不清正确的类型是什么”
**回应:**暂时使用any类型,但要约束输出类型。
**行动:**添加显式返回类型。添加中间变量的类型注解。
3. "It's Internal Code"
3. “这是内部代码”
Pressure: "No external code uses this"
Response: Your future self is external code.
Action: Scope the any narrow. Document why it exists.
压力:“没有外部代码会用到这个”
**回应:**未来的你就是“外部代码”。
**行动:**缩小any类型的作用域。记录使用any的原因。
Red Flags - STOP and Reconsider
危险信号——立即停止并重新考虑
- any on function parameters
- Functions returning any (especially without explicit return type)
- any assigned to a variable used multiple times
- any spreading through expressions
- Multiple functions passing any between them
- 函数参数使用any类型
- 返回any类型的函数(尤其是没有显式返回类型的)
- any类型赋值给一个被多次使用的变量
- any类型在表达式中扩散
- 多个函数之间传递any类型
Common Rationalizations (All Invalid)
常见的不合理借口(全无效)
| Excuse | Reality |
|---|---|
| "It's contained" | any spreads through returns and assignments. |
| "Only used once" | That's one bug waiting to happen. |
| "I'll fix it later" | Later never comes. Scope it now. |
| "The function is short" | Short functions can still spread any. |
| 借口 | 事实 |
|---|---|
| “它已经被控制住了” | any类型会通过返回值和赋值扩散。 |
| “只用一次而已” | 这就是一个潜在的bug。 |
| “我之后再修复” | 之后永远不会来。现在就缩小其作用域。 |
| “这个函数很短” | 短函数仍然会扩散any类型。 |
Quick Reference
快速参考
| any Situation | Narrowing Technique |
|---|---|
| any parameter | Add return type annotation |
| any return value | Add explicit return type |
| any in expression | Assign to typed variable |
| any from JSON.parse | Add return type or validate |
| any from library | Assert to specific type |
| any类型场景 | 缩小作用域技巧 |
|---|---|
| any类型参数 | 添加返回类型注解 |
| any类型返回值 | 添加显式返回类型 |
| 表达式中的any类型 | 赋值给带类型的变量 |
| JSON.parse返回的any类型 | 添加返回类型或进行验证 |
| 第三方库返回的any类型 | 断言为特定类型 |
Auditing any Usage
审查any类型的使用
typescript
// 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
}typescript
// 之前:到处都是any类型
function process(data: any): any {
const x = data.foo; // any类型
const y = data.bar; // any类型
return { x, y }; // any类型
}
// 之后:any类型被控制
function process(data: any): Result {
const x: number = data.foo; // number类型
const y: string = data.bar; // string类型
return { x, y }; // Result类型
}The Bottom Line
核心要点
Treat any like a hazardous material. Contain the spill.
If you must use any, use it on the smallest possible piece of code. Add type annotations to stop it from spreading. Your goal is type safety everywhere except the one line that needs any.
将any类型视为危险物质,控制其扩散范围。
如果必须使用any类型,请将其用于代码中最小的必要部分。添加类型注解以阻止它扩散。你的目标是除了那一行必须使用any的代码外,其余所有代码都保持类型安全。
Reference
参考资料
Based on "Effective TypeScript" by Dan Vanderkam, Item 43: Use the Narrowest Possible Scope for any Types.
基于Dan Vanderkam所著《Effective TypeScript》中的第43条:Use the Narrowest Possible Scope for any Types。