TypeScript 从入门到放弃any:老大说再写 any 就扣钱!

今天,代码评审现场

老大:"小杜啊,你这 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 的三大罪状

  1. 类型检查失效:编译器直接躺平
  2. 智能提示消失:IDE 变成记事本
  3. 重构地狱:改一处崩十处

二、替代 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 的使用。

你学会了吗?

相关推荐
爷_2 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
charlee443 小时前
行业思考:不是前端不行,是只会前端不行
前端·ai
Amodoro4 小时前
nuxt更改页面渲染的html,去除自定义属性、
前端·html·nuxt3·nuxt2·nuxtjs
Wcowin5 小时前
Mkdocs相关插件推荐(原创+合作)
前端·mkdocs
伍哥的传说5 小时前
CSS+JavaScript 禁用浏览器复制功能的几种方法
前端·javascript·css·vue.js·vue·css3·禁用浏览器复制
lichenyang4535 小时前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself2436 小时前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
三口吃掉你6 小时前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat
Trust yourself2436 小时前
在easyui中如何设置自带的弹窗,有输入框
前端·javascript·easyui
烛阴6 小时前
Tile Pattern
前端·webgl