提升 Web 开发效率:我常用的 10 个 TypeScript 实战技巧

欢迎关注微信公众号:FSA全栈行动 👋

Web 的时候,如果只是把 TypeScript 当成一个单纯的"报错检查器",那就太浪费了。在实际的工程实践中,TypeScript 能帮我们写出更安全、更简洁的代码,甚至能帮我们在编译阶段就拦住很多低级的逻辑错误。

我自己在写 Web 项目时,总结了这 10 个非常实用的技巧,希望能帮大家提升开发体验。

一、基础进阶:减少冗余与提升准确性

在处理基础类型定义时,很多小伙伴容易写出重复且不精确的代码。

1、用 satisfies 替代类型断言

以前在给对象指定类型时,我经常会习惯性地使用 as。但 as 实际上是一种"强行告诉编译器听我的"行为,它会掩盖掉一些潜在的配置错误。

现在我更倾向于使用 satisfies,它既能校验类型,又能保留对象最原始的字面量信息。

方式 校验能力 类型精度 风险
使用 as 强行转换 丢失具体的字面量信息,降级为宽泛类型 容易掩盖错误的配置值
使用 satisfies 严格校验 保留最精确的字面量类型 安全且准确
TypeScript 复制代码
// 不推荐:使用 as
const config = {
  theme: "dark",
  language: "en",
} as AppConfig;
// 如果 AppConfig 要求 theme 必须是 "light",这里可能不会报错,但运行时逻辑会乱

// 推荐:使用 satisfies
const config = {
  theme: "dark",
  language: "en",
} satisfies AppConfig;
// 如果配置不符合 AppConfig,这里会直接报错

2、利用 typeof 复用类型

有时候我们定义了一个复杂的对象,但随后又想根据这个对象生成一个 interface。与其手写一遍几乎一模一样的结构,不如直接用 typeof

TypeScript 复制代码
const user = {
  id: 1,
  name: "John",
};

// 直接复用对象的结构,不需要再手动写一遍 Interface
type User = typeof user;

3、用 keyof 提取键名

在维护大型 interface 时,如果需要定义一组字段的集合,千万别手动去写 type Fields = "id" | "name" | ...。这种方式一旦后续 interface 增加了属性,你还得手动去更新这个 type,非常容易漏掉。

TypeScript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
}

// 自动获取所有属性名,增加新属性时这里会自动同步
type UserFields = keyof User;

4、利用 as const 固定字面量

在定义常量配置时,TypeScript 默认会把值推断为宽泛的 string 类型。但在处理 Reduxaction 或者一些状态机时,我们需要它保持精确的字面量值。

TypeScript 复制代码
// 没有 as const 时,STATUS.SUCCESS 的类型是 string
const STATUS = {
  SUCCESS: "success",
};

// 使用 as const 后,STATUS.SUCCESS 的类型就是 "success"
const STATUS = {
  SUCCESS: "success",
} as const;

5、能推断就不要手动标类型

这是一个很小的习惯,但能让代码看起来清爽很多。TypeScript 的推断能力其实非常强,没必要在每个地方都写上 const count: number = 10;

TypeScript 复制代码
// 没必要这样写
const count: number = 10;

// 直接写,编译器自己心里有数
const count = 10;

二、灵活运用类型工具:应对业务场景

在处理 API 数据或组件 Props 时,RecordPartialPickOmit 是绝对的生产力工具。

1、使用 Record 构建映射关系

当你需要定义一个键值对对象,且键的范围是固定的(比如各种状态的颜色配置)时,Record 非常好用。它能强制要求你必须实现所有的键,避免漏掉某个状态。

TypeScript 复制代码
type Status = "success" | "warning" | "danger";

// 强制要求你必须写全这三种状态对应的颜色
const colors: Record<Status, string> = {
  success: "#22c55e",
  warning: "#f59e0b",
  danger: "#ef4444",
};

2、使用 Partial 处理局部更新

在做编辑页面或者 PATCH 请求时,我们通常只需要提交部分字段。这时候用 Partial 可以快速生成一个"所有属性都变为可选"的类型。

TypeScript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
}

// type UpdateUser = { id?: number; name?: string; email?: string; }
type UpdateUser = Partial<User>;

const updateUser = (data: UpdateUser) => {
  // ...
};

3、用 PickOmit 精简接口

有时候一个 interface 太重了(包含了敏感信息或不需要的字段),我们可以通过 Pick(挑选)或 Omit(剔除)来快速得到一个轻量版的类型。

TypeScript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// 只要 email 和 password,用于登录
type LoginUser = Pick<User, "email" | "password">;

// 排除掉 password,用于公开展示
type PublicUser = Omit<User, "password">;

三、进阶实战:通用组件与严谨逻辑

1、通过 Generics 构建高度复用的组件

如果你写了一个 Select 选择框组件,希望它既能渲染用户列表,又能渲染商品列表,那么 Generics(泛型)就是唯一的选择。

TypeScript 复制代码
interface SelectProps<T> {
  items: T[];
  renderItem(item: T): React.ReactNode;
}

// 这样写,无论传入什么类型的 items,组件都能精准捕获类型
function MySelect<T>({ items, renderItem }: SelectProps<T>) {
  return (
    <ul>
      {items.map((item, idx) => (
        <li key={idx}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

2、用联合类型规避错误的 Prop 组合

这是一个很有意思的场景。假设你写一个 Button 组件,它要么是一个链接(有 href 属性),要么是一个普通按钮(有 onClick 属性),但逻辑上不应该两者都有。

如果用传统的可选属性 href?: string; onClick?: () => void;,开发者可能会同时传这两个属性,导致逻辑混乱。我们可以用联合类型来锁定这种"互斥"关系。

TypeScript 复制代码
type ButtonProps =
  | { href: string; onClick?: never }
  | { onClick: () => void; href?: never };

// 这样定义后:
// <Button href="/home" /> -> OK
// <Button onClick={handleClick} /> -> OK
// <Button href="/home" onClick={handleClick} /> -> Error! (TS 会报错)

最后

TypeScript 的核心价值不仅仅在于抓 Bug,更在于它提供了一种方式,让我们能用"声明式"的方式去描述业务逻辑和组件边界。

这 10 个技巧不需要一次性全部学会,我建议大家在开发过程中,当你觉得自己在写重复的类型定义,或者觉得某个组件的 Props 边界模糊时,就回过头来看看这套方案。

如果你觉得这篇内容对你有帮助,欢迎在评论区交流。

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~

相关推荐
勇往直前plus3 小时前
Vue3(篇四)单页面应用到路由管理
javascript·typescript·前端框架·vue
勇往直前plus5 小时前
Vue3(篇三) Element Plus
前端·javascript·typescript·vue
AI_paid_community5 小时前
机械研发人员如何使用 Claude?
java·vue.js
cdcdhj5 小时前
vue3中的watchEffect()监听,什么时候监听,什么时候清理,什么时候停止
前端·javascript·vue.js
无人生还5 小时前
从 Vue3 到 React · 快速上手系列第 6 篇:状态管理 useState 与 useReducer
前端·vue.js·react.js
退休倒计时5 小时前
【每日一题】LeetCode 207. 课程表 TypeScript
算法·leetcode·typescript
林间码客7 小时前
全栈视角下的 Mock 指南:从后端单元测试到前端 Vue 3 联调
前端·vue.js·单元测试
张元清7 小时前
React usePrevious Hook:追踪上一次的 State 和 Props(2026)
javascript·react.js
Cobyte7 小时前
手写 Rollup 插件实现解析 Vue 文件
前端·javascript·vue.js