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 小时前
前端JavaScript笔记之父子组件数据传递,watch用法之对象形式监听器的核心handler函数
前端·javascript·笔记
开发加微信:hedian1163 小时前
短剧小程序开发全攻略:从技术选型到核心实现(前端+后端+运营干货)
前端·微信·小程序
YCOSA20255 小时前
ISO 雨晨 26200.6588 Windows 11 企业版 LTSC 25H2 自用 edge 140.0.3485.81
前端·windows·edge
小白呀白5 小时前
【uni-app】树形结构数据选择框
前端·javascript·uni-app
吃饺子不吃馅5 小时前
深感一事无成,还是踏踏实实做点东西吧
前端·svg·图形学
90后的晨仔6 小时前
Mac 上配置多个 Gitee 账号的完整教程
前端·后端
少年阿闯~~6 小时前
CSS——实现盒子在页面居中
前端·css·html
开发者小天6 小时前
uniapp中封装底部跳转方法
前端·javascript·uni-app
阿波罗尼亚7 小时前
复杂查询:直接查询/子查询/视图/CTE
java·前端·数据库
正义的大古7 小时前
OpenLayers地图交互 -- 章节九:拖拽框交互详解
前端·vue.js·openlayers