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 的使用。

你学会了吗?

相关推荐
Hooray13 小时前
前端暗黑模式的适配艺术
前端·vue.js·视觉设计
恋猫de小郭13 小时前
解析华为 DevEco Code 和小米 MiMo Code,都基于 OpenCode ,有什么区别?
android·前端·ios
IT_陈寒13 小时前
Vue的响应式让我原地裂开,你们也有这情况吗
前端·人工智能·后端
问心无愧051313 小时前
ctfshow web入门114
android·前端·笔记
晓得迷路了13 小时前
栗子前端技术周刊第 133 期 - Angular v22、React 编译器 Rust 版、pnpm 11.5...
前端·javascript·css
一个被程序员耽误的厨师14 小时前
02-架构篇-前端怎么反客为主把AI编排权拿回到自己手里
前端·人工智能·架构
羊羊小栈14 小时前
基于混合检索RAG的食品生产质量问答系统(BGE_BM25_大语言模型)
前端·人工智能·语言模型·自然语言处理·毕业设计·大作业
烤代码的吐司君14 小时前
Redis 服务配置与使用
前端·bootstrap·html
之歆14 小时前
Ajax 基础技术深度解析:XHR 从入门到跨域
前端·ajax·okhttp
怕浪猫14 小时前
Electron 开发实战(十四):实战项目|从零搭建轻量化桌面代码编辑器
前端·electron·node.js