precise-any-variants
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePrefer More Precise Variants of any to Plain any
优先使用更精确的any变体而非普通any
Overview
概述
If you must use any, make it as specific as possible.
Plain accepts everything. But , , or are narrower and still provide some type checking.
anyany[]Record<string, any>() => any如果你必须使用any,请尽可能让它更具体。
普通的接受所有类型,但、或的范围更窄,同时仍能提供一定的类型检查。
anyany[]Record<string, any>() => anyWhen to Use This Skill
何时使用此技巧
- Forced to use any for some reason
- Writing functions that accept "anything"
- Dealing with truly dynamic data
- Migrating from JavaScript
- 因某些原因不得不使用any
- 编写接受“任意内容”的函数
- 处理真正动态的数据
- 从JavaScript迁移到TypeScript
The Iron Rule
铁则
any is a last resort.
Specific variants of any preserve partial type safety.Remember:
- checks that it's an array
any[] - checks that it's an object
Record<string, any> - checks that it's a function
() => any - is even safer than any variant
unknown
any是最后的选择。
更具体的any变体可保留部分类型安全性。请记住:
- 会检查是否为数组
any[] - 会检查是否为对象
Record<string, any> - 会检查是否为函数
() => any - 比任何any变体都更安全
unknown
Detection: Over-broad any
检测:过于宽泛的any
typescript
function getLength(x: any) { // Too broad!
return x.length;
}
getLength(123); // No error, crashes at runtime
getLength(null); // No error, crashes at runtime
getLength([1,2,3]); // OKtypescript
function getLength(x: any) { // 过于宽泛!
return x.length;
}
getLength(123); // 无报错,但运行时会崩溃
getLength(null); // 无报错,但运行时会崩溃
getLength([1,2,3]); // 正常Better: Specific any Variants
更好的选择:具体的any变体
any[] for Arrays
用any[]表示数组
typescript
function getLength(array: any[]) {
return array.length; // Type checked!
}
getLength([1, 2, 3]); // OK
getLength(/regex/);
// ~~~~~~~
// Argument of type 'RegExp' is not assignable to parameter of type 'any[]'Benefits:
- access is type-checked
.length - Return type is , not
numberany - Non-arrays are rejected
typescript
function getLength(array: any[]) {
return array.length; // 已做类型检查!
}
getLength([1, 2, 3]); // 正常
getLength(/regex/);
// ~~~~~~~
// 类型“RegExp”的参数不能赋给类型“any[]”的参数优势:
- 的访问会被类型检查
.length - 返回类型为,而非
numberany - 非数组会被拒绝
Record<string, any> for Objects
用Record<string, any>表示对象
typescript
function hasKey(obj: Record<string, any>, key: string): boolean {
return key in obj;
}
hasKey({ a: 1 }, 'a'); // OK
hasKey(null, 'a');
// ~~~~
// Argument of type 'null' is not assignable to parameter of type 'Record<string, any>'typescript
function hasKey(obj: Record<string, any>, key: string): boolean {
return key in obj;
}
hasKey({ a: 1 }, 'a'); // 正常
hasKey(null, 'a');
// ~~~~
// 类型“null”的参数不能赋给类型“Record<string, any>”的参数() => any for Functions
用() => any表示函数
typescript
type Fn0 = () => any; // No params
type Fn1 = (arg: any) => any; // One param
type FnN = (...args: any[]) => any; // Any params
function callTwice(fn: FnN) {
fn();
fn();
}
callTwice(() => console.log('hi')); // OK
callTwice(123);
// ~~~
// Argument of type 'number' is not assignable to parameter of type '(...args: any[]) => any'typescript
type Fn0 = () => any; // 无参数
type Fn1 = (arg: any) => any; // 一个参数
type FnN = (...args: any[]) => any; // 任意数量参数
function callTwice(fn: FnN) {
fn();
fn();
}
callTwice(() => console.log('hi')); // 正常
callTwice(123);
// ~~~
// 类型“number”的参数不能赋给类型“(...args: any[]) => any”的参数Use any[] for Rest Parameters
用any[]表示剩余参数
typescript
// any rest parameter: return type is any
const numArgsBad = (...args: any) => args.length;
// ^? (...args: any) => any
// any[] rest parameter: return type is number
const numArgsBetter = (...args: any[]) => args.length;
// ^? (...args: any[]) => numberThe return type matters for downstream code!
typescript
// any剩余参数:返回类型为any
const numArgsBad = (...args: any) => args.length;
// ^? (...args: any) => any
// any[]剩余参数:返回类型为number
const numArgsBetter = (...args: any[]) => args.length;
// ^? (...args: any[]) => number返回类型对下游代码至关重要!
Comparison of any Variants
any变体对比
| Type | Accepts | Rejects |
|---|---|---|
| Everything | Nothing |
| Arrays | Non-arrays |
| Objects | Primitives, null |
| Functions | Non-functions |
| Objects, arrays | Primitives, null |
| Everything | Everything (without check) |
| 类型 | 接受 | 拒绝 |
|---|---|---|
| 所有内容 | 无 |
| 数组 | 非数组 |
| 对象 | 原始类型、null |
| 函数 | 非函数 |
| 对象、数组 | 原始类型、null |
| 所有内容 | 所有内容(未检查时) |
Consider unknown Instead
考虑使用unknown替代
unknowntypescript
function process(data: unknown) {
// Must narrow before using
if (Array.isArray(data)) {
data.length; // OK, data is any[]
}
if (typeof data === 'object' && data !== null) {
// data is object
}
}With , you can't do anything without first checking the type.
unknownunknowntypescript
function process(data: unknown) {
// 使用前必须先收窄类型
if (Array.isArray(data)) {
data.length; // 正常,data类型为any[]
}
if (typeof data === 'object' && data !== null) {
// data类型为object
}
}使用时,你必须先检查类型才能进行操作。
unknownReal-World Example: JSON Parsing
实际案例:JSON解析
typescript
// Don't return any
function parseBad(json: string): any {
return JSON.parse(json);
}
// Better: return unknown
function parseGood(json: string): unknown {
return JSON.parse(json);
}
// Caller must narrow:
const data = parseGood('{"x": 1}');
if (typeof data === 'object' && data !== null && 'x' in data) {
console.log(data.x);
}typescript
// 不要返回any
function parseBad(json: string): any {
return JSON.parse(json);
}
// 更好的选择:返回unknown
function parseGood(json: string): unknown {
return JSON.parse(json);
}
// 调用者必须收窄类型:
const data = parseGood('{"x": 1}');
if (typeof data === 'object' && data !== null && 'x' in data) {
console.log(data.x);
}When any is Unavoidable
当any不可避免时
Sometimes you genuinely need any:
typescript
// Wrapping a library that uses any internally
function wrapLibrary<T>(input: T): T {
return (library as any).process(input);
}
// Type assertion in implementation
function merge<T>(a: Partial<T>, b: Partial<T>): T {
return { ...a, ...b } as any as T;
}Even then, hide it inside functions with good type signatures (Item 45).
有时你确实需要使用any:
typescript
// 封装一个内部使用any的库
function wrapLibrary<T>(input: T): T {
return (library as any).process(input);
}
// 实现中的类型断言
function merge<T>(a: Partial<T>, b: Partial<T>): T {
return { ...a, ...b } as any as T;
}即便如此,也要把它隐藏在具有良好类型签名的函数内部(参考第45条)。
Pressure Resistance Protocol
压力应对方案
1. "any Works"
1. “any能用就行”
Pressure: "Just use any and move on"
Response: any disables ALL type checking. Specific variants preserve some safety.
Action: Use the most specific variant that works.
压力:“直接用any就行,继续往下做”
**回应:**any会禁用所有类型检查。具体的变体可保留部分安全性。
**行动:**使用能满足需求的最具体变体。
2. "I Don't Know the Type"
2. “我不知道类型”
Pressure: "The type is truly dynamic"
Response: unknown is safer for truly unknown types.
Action: Use unknown, then narrow before using.
压力:“类型确实是动态的”
**回应:**对于真正未知的类型,unknown更安全。
**行动:**使用unknown,然后在使用前收窄类型。
Red Flags - STOP and Reconsider
危险信号 - 停止并重新考虑
- as function parameter (use specific variant)
any - as return type (prefer unknown)
any - for objects (use Record<string, any> or object)
any - for arrays (use any[])
any
- 将作为函数参数(使用具体变体)
any - 将作为返回类型(优先使用unknown)
any - 用表示对象(使用Record<string, any>或object)
any - 用表示数组(使用any[])
any
Common Rationalizations (All Invalid)
常见的合理化借口(均无效)
| Excuse | Reality |
|---|---|
| "It's too dynamic to type" | unknown handles truly dynamic data |
| "Specific variants are verbose" | A few characters save runtime errors |
| "any is fine for internal code" | Internal code still has bugs |
| 借口 | 事实 |
|---|---|
| “它太动态了,无法定义类型” | unknown可以处理真正动态的数据 |
| “具体变体太啰嗦” | 多写几个字符能避免运行时错误 |
| “内部代码用any没问题” | 内部代码也会有bug |
Quick Reference
快速参考
typescript
// DON'T: Plain any
function f(x: any): any { ... }
// DO: Specific variants
function getLength(arr: any[]): number { ... }
function getKeys(obj: Record<string, any>): string[] { ... }
function call(fn: (...args: any[]) => any): void { ... }
// BEST: unknown when possible
function process(data: unknown): void {
if (Array.isArray(data)) { ... }
}typescript
// 不要:普通any
function f(x: any): any { ... }
// 要:具体变体
function getLength(arr: any[]): number { ... }
function getKeys(obj: Record<string, any>): string[] { ... }
function call(fn: (...args: any[]) => any): void { ... }
// 最佳选择:尽可能使用unknown
function process(data: unknown): void {
if (Array.isArray(data)) { ... }
}The Bottom Line
总结
If you must use any, make it specific.
any[]Record<string, any>() => anyunknown如果你必须使用any,请让它更具体。
any[]Record<string, any>() => anyunknownReference
参考资料
Based on "Effective TypeScript" by Dan Vanderkam, Item 44: Prefer More Precise Variants of any to Plain any.
基于Dan Vanderkam所著《Effective TypeScript》中的第44条:优先使用更精确的any变体而非普通any。