今天,代码评审现场
老大:"小杜啊,你这
getUserInfo()
返回 any 是几个意思?"我:"嘿嘿,这样就不用写类型校验了..."
老大(核善微笑):"从今天起,谁在代码里写 any ------ 一次罚 100!"
会议室温度骤降 10℃ ❄️ 本来就冷,一会儿就起了好多鸡皮疙瘩....
玩笑归玩笑,老大铁令背后藏着真理:any 是 TypeScript 的类型系统漏洞。它像代码中的「混沌恶魔」,吞噬类型安全,让编译检查形同虚设。今天我们就来根治 any 依赖症!
一、为什么 any 是万恶之源?
typescript
// 🚨 灾难现场示例
function fetchData(): any {
return JSON.parse(localStorage.getItem("data") || "null");
}
const result = fetchData();
result.undefinedMethod(); // 编译通过!运行时爆炸💥
any 的三大罪状:
- 类型检查失效:编译器直接躺平
- 智能提示消失:IDE 变成记事本
- 重构地狱:改一处崩十处
二、替代 any 的四大神器
🛡️ 神器 1:unknown
------ 安全的 any
typescript
function safeParse(json: string): unknown {
return JSON.parse(json);
}
const data = safeParse('{"name": "Alice"}');
// data.name // 🚫 错误!必须先类型检查
// 类型收窄 (Type Narrowing)
if (data && typeof data === "object" && "name" in data) {
console.log(data.name.toUpperCase()); // ✅ 安全
}
unknown
生存法则:
- 所有类型都能赋给
unknown
unknown
只能赋给any
/unknown
- 使用时必须显式断言或类型收窄
🧩 神器 2:泛型 ------ 动态类型模板
typescript
// 泛型函数示例
function identity<T>(arg: T): T {
return arg;
}
const num = identity(42); // 自动推断为 number
const str = identity("TS"); // 自动推断为 string
// 泛型约束
function getProp<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
getProp({ name: "Bob", age: 30 }, "age"); // ✅
getProp({ name: "Bob", age: 30 }, "email"); // 🚫 编译报错!
🔧 神器 3:工具类型 ------ 类型变形金刚
工具类型 | 作用 | 示例 |
---|---|---|
Partial<T> |
所有属性变为可选 | Partial<{ id: number }> → { id?: number } |
Required<T> |
所有属性变为必填 | Required<{ id?: number }> → { id: number } |
Readonly<T> |
所有属性变为只读 | Readonly<{ id: number }> → { readonly id: number } |
Pick<T, K> |
选取部分属性 | Pick<User, "name"|"age"> |
Omit<T, K> |
排除部分属性 | Omit<User, "password"> |
Record<K, V> |
构造键类型为K的对象 | Record<"id"|"code", string> |
实战组合技:
typescript
type User = {
id: number;
name: string;
age?: number;
email: string;
};
// 创建用户时所有属性可选,但邮箱必填
type CreateUserDto = Partial<User> & { email: string };
// API响应:排除敏感字段,添加时间戳
type SafeUser = Omit<User, "password"> & { createdAt: Date };
🚦 神器 4:类型守卫 ------ 智能类型过滤
typescript
// 自定义类型守卫
function isUser(data: unknown): data is User {
return !!data &&
typeof data === "object" &&
"id" in data &&
"name" in data;
}
// 使用守卫
const data: unknown = fetchData();
if (isUser(data)) {
console.log(`Hello, ${data.name}`); // ✅ 自动识别为 User 类型
}
三、特殊类型补完计划
1. void
------ 无返回值函数
typescript
function logMessage(msg: string): void {
console.log(msg);
// 不需要 return
}
2. never
------ 永不抵达的终点
typescript
// 场景1:抛出错误
function throwError(msg: string): never {
throw new Error(msg);
}
// 场景2:死循环
function infiniteLoop(): never {
while(true) { /* ... */ }
}
// 场景3:类型穷尽检查
type Shape = "circle" | "square";
function handleShape(shape: Shape) {
switch(shape) {
case "circle": /*...*/ break;
case "square": /*...*/ break;
default:
const _exhaustiveCheck: never = shape; // 如果新增类型会报错!
}
}
四、终极防御:TSConfig 核武器
在 tsconfig.json
中启用大杀器:
json
{
"compilerOptions": {
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true, // 严格空检查
"noUncheckedIndexedAccess": true // 索引访问检查
}
}
开启后,任何试图潜入项目的 any 都会在编译阶段被当场击毙!🔫
人类与 any 的战争永无止境
某次代码评审后------
老大:"这次为什么不用 any?"
我(骄傲状):"用了 unknown + 泛型 + 类型守卫!"
老大(点头):"很好...但为什么
user.id
可能是undefined
?"我:"😱 我这就去加
NonNullable
!"
记住:每一次拒绝 any,都是对 Bug 的精准爆头。 你的类型越精确,半夜被报警电话吵醒的概率就越低。现在,是时候和 any 彻底说再见了!
作者注:本文撰写后,团队 any 使用量下降 98%,老大欣慰地取消了罚款制度...然后开始抓
@ts-ignore
的使用。
你学会了吗?