每位前端开发者都应该掌握的 5 个 TypeScript 技巧

当大多数前端开发者开始使用 TypeScript 时,常常会感觉我们在不断地与类型系统作斗争 —— 到处都是错误、红色波浪线,以及一开始看起来毫无意义的规则。但一旦你理解了一些关键模式,一切都会改变。TypeScript 不再是一个问题,而开始成为你最大的编码优势。
在这篇文章中,我将分享 5 个实用的 TypeScript 技巧,它们可以彻底改变你编写和理解代码的方式。
让我们从第一个技巧开始。
1. 使用 as const 收窄类型
让我们从一个几乎让所有 TypeScript 初学者感到困惑的问题开始。
TypeScript 有时表现得像一个过度保护的家长。即使你明确地写了一个值,比如 “GET”,它仍然只把它当作一个普通的 string,而不是精确的值 “GET”。
问题也正是从这里开始的。
想象一下你对某人说:
“我要带一个红色的球。”
但对方回答:
“好的……你要带一个球。”
这正是 TypeScript 的默认行为。它忽略了“红色”这一部分,只记住了“球”。
但有时候,你的函数只接受红色或蓝色的球——而不是任意的球。
因此,当 TypeScript 把 “GET” 仅仅当作 “string” 时,你的函数可能会报错。
问题
function makeRequest(method: "GET" | "POST") {
console.log("Request method:", method);
}
const request = {
method: "GET"
};
makeRequest(request.method);
// ❌ Error: Type 'string' is not assignable to type '"GET" | "POST"'

为什么会出现这个错误?
因为 TypeScript 将 “GET” 扩宽成了一个通用的 string。它忘记了这个值实际上是一个特定的 “GET”。
解决方案 —— as const
我们告诉 TypeScript:
“不要对它进行泛化。按原样精确对待它。”
const request = {
method: "GET"
} as const;
makeRequest(request.method);
// ✅ Works perfectly

现在 TypeScript 明白了:
- 这个值是固定的
- 它是只读的
- 它就是精确的 “GET” —— 而不是任意的 string
as const 实际上做了什么?
当你添加 as const 时,TypeScript:
1.锁定值(只读)

2.保留精确的字面量类型
3.防止被扩宽为 string、number 等类型
"GET" → 保持 "GET" 42 → 保持 42 true → 保持 true
使用案例
这在配置文件、路由或常量数据中非常有用。
const routes = {
home: "/",
about: "/about",
contact: "/contact"
} as const;
现在 TypeScript 会自动知道这些精确的值。
你甚至可以从中生成类型:
type Route = typeof routes[keyof typeof routes]; // "/" | "/about" | "/contact"

- 不需要手动定义类型;
- 没有重复;
- 一切保持同步。
何时应该使用它
在以下情况下使用as const:
- 定义配置对象时;
- API 方法常量;
- 路由;
- 状态码;
- action 类型(Redux 等)。
本质上就是:任何值不应该改变且必须保持精确的地方
2. 判别联合类型
这是 TypeScript 中最强大的模式之一,同时也是初学者最容易误解的之一。
它解决了一个非常常见的真实问题:处理那些看起来相似,但会根据自身状态表现出不同行为的对象。
例如:
- loading 状态;
- success 状态;
- error 状态。
想象一个交通灯,它有 3 种状态:
- 红灯 → 停止;
- 黄灯 → 等待;
- 绿灯 → 通行。
你不会用同样的方式对待它们。
你不会说:
“也许停……也许走……也许等……”
每种状态都有明确的身份。
这正是 TypeScript 希望你的对象具备的方式。
初学者常犯的错误
大多数初学者会像这样创建一个巨大的接口:
interface ApiResponse {
status: string;
data?: string;
error?: string;
}
看起来很简单,但它会引发混乱。
现在 TypeScript 无法知道:
- 什么时候 data 存在
- 什么时候 error 存在
- 哪些是可以安全使用的
你可能会在错误状态下误访问 data。
这种方式下 TypeScript 无法很好地保护你。
更好的方式 —— 判别联合类型
与其使用一个混乱的接口,不如创建独立的状态。
interface LoadingState {
status: "loading";
}
interface SuccessState {
status: "success";
data: string;
}
interface ErrorState {
status: "error";
error: string;
}
type ApiResponse = LoadingState | SuccessState | ErrorState;
现在每种状态都是清晰且可预测的。
TypeScript 是如何变得“智能”的?
function handleResponse(response: ApiResponse) {
if (response.status === "success") {
console.log(response.data);
// ✅ TypeScript 知道 data 存在
}
if (response.status === "error") {
console.log(response.error);
// ✅ TypeScript 知道 error 存在
}
}
TypeScript 会自动理解:
- 在 success 中 → data 可用;
- 在 error 中 → error 可用;
- 在 loading 中 → 两者都不存在。
没有混乱。
不需要猜测。
没有不安全的代码。
为什么这种模式在实际应用中很强大
你会在很多地方使用它:
- API 响应;
- 认证状态;
- UI 状态(loading、empty、success、failure);
- reducer(Redux、Zustand 等);
- 支付状态;
- 表单校验流程。
这种模式会让你的应用变得:
- 更安全;
- 更容易调试;
- 更容易扩展。
而 TypeScript 会成为你的助手,而不是你的对手。
3. 使用 satisfies 替代类型注解
这是 TypeScript 中一个较新的特性,一旦你理解了它,你就会在各处使用它。
它解决了一个微妙但令人沮丧的问题:在不丢失对象具体细节的情况下,对对象的结构进行校验。
想象一位老师在检查你的作业。
他们不会重写你的答案。他们只是检查:
“是的,这符合规则。”
这正是 satisfies 所做的事情。
它会检查你的对象是否符合所需的结构,但不会改变你最初写下的内容。
普通类型注解的问题
当你这样写时:
type ButtonVariant = "primary" | "secondary"; const button: ButtonVariant = "primary";
TypeScript 只会记住:
这是允许的变体之一
但如果你使用对象:
type ButtonConfig = {
variant: "primary" | "secondary";
};
const config: ButtonConfig = {
variant: "primary"
};
TypeScript 会发生类型扩宽。
它关注的是匹配类型,而不是保留精确的字面量细节。
更好的方式 —— satisfies
type ButtonConfig = {
variant: "primary" | "secondary";
};
const config = {
variant: "primary"
} satisfies ButtonConfig;
现在会发生两件事:
- TypeScript 会检查该对象是否符合
ButtonConfig; - 它仍然会记住精确的值 “primary”。
因此你获得:
- 校验;
- 安全性;
- 精确性。
它在什么情况下会变得非常有用
当处理配置对象、路由或映射时。
例如:
type Routes = Record<string, string>;
const routes = {
home: "/",
about: "/about",
contact: "/contact"
} satisfies Routes;
现在 TypeScript:
- 确保所有值都是 string;
- 保留精确的键名;
- 保留字面量类型。
防止拼写错误
satisfies 可以捕获那些简单类型注解可能无法清晰提示的错误。
type ButtonConfig = {
variant: "primary" | "secondary";
};
const config = {
varient: "primary" // ❌ 拼写错误
} satisfies ButtonConfig;

TypeScript 会立即标记:
缺少属性 ‘variant’
这可以防止配置中出现悄无声息的 bug。
为什么开发者喜欢它
因为它同时具备两方面的优势:
- 严格的结构校验;
- 精确的字面量推断;
- 更安全的配置文件;
- 更好的自动补全;
- 避免不必要的类型扩宽。
尤其适用于:
- 设计系统配置;
- 路由定义;
- feature flags;
- 常量映射;
- UI 组件变体。
4. 模板字面量类型
大多数初学者认为 TypeScript 只适用于数字、对象和函数。
但其实 TypeScript 也可以控制字符串应该是什么样子。
是的,甚至字符串格式也可以是类型安全的。
想象你的老师说:
“你只能按照这种格式写答案:数字 + 单位。”
这意味着:
- ✅ 10px
- ✅ 2rem
- ❌ 10
- ❌ px
模板字面量类型允许 TypeScript 强制执行这样的规则。
它是如何工作的
你可以使用反引号组合类型:
type CSSUnit = "px" | "rem" | "em";
type Size = `${number}${CSSUnit}`;
现在只允许符合模式的值。
示例 —— CSS 值:
function setWidth(size: Size) {
console.log(size);
}
setWidth("20px"); // ✅ 合法
setWidth("2rem"); // ✅ 合法
setWidth("20"); // ❌ 错误
setWidth("20pt"); // ❌ 不支持的单位

TypeScript 会检查格式,而不仅仅是类型。
这可以在运行时之前防止 bug。
真实使用场景 —— API 接口
你可以安全地生成结构化字符串。
type Method = "GET" | "POST";
type Version = "v1" | "v2";
type Resource = "users" | "posts";
type ApiEndpoint = `${Method}/${Version}/${Resource}`;
现在:
const endpoint: ApiEndpoint = "GET/v1/users"; // ✅ 合法 const wrong: ApiEndpoint = "FETCH/v1/users"; // ❌ 不合法

你是在类型层面定义 API 规则。
不会有无效的 API 接口漏过。
为什么这很强大
因为应用中的许多 bug 都来自错误的字符串格式:
- CSS 值;
- URL;
- API 路由;
- class 名;
- ID;
- 事件名。
模板字面量类型为你提供:
- 格式安全;
- 自动补全;
- 零运行时校验;
- 更少的生产环境 bug。
你会在真实项目中使用它的地方
- 设计系统;
- API 客户端;
- 路由生成器;
- 动态 class 名;
- 基于配置的 UI。
对于一些大型团队,命名模式会显得非常重要,这个时候模板字面量类型就尤为有用。
5. 必备的工具类型与常见 TypeScript 陷阱
这个最后的技巧就像一个工具箱。
TypeScript 已经为你提供了许多内置的辅助工具,但大多数初学者要么:
- 不知道它们的存在;
- 误解了它们的行为。
这就会导致 bug、困惑以及混乱的类型。
让我们把它们简单拆解一下。
你必须了解的工具类型
可以把工具类型看作是对类型进行编辑的工具。
你不需要重写一切,只需调整你需要的部分。
1)Required —— 让所有属性变为必填
type User = {
name?: string;
age?: number;
};
type RequiredUser = Required<User>;
现在:
- name → 必填;
- age → 必填。

适用于以下情况:
- 在保存之前数据必须完整;
- 表单提交校验;
- API 请求体准备。
2)Partial —— 让所有属性变为可选
type User = {
name: string;
age: number;
};
type UpdateUser = Partial<User>;

现在你可以只更新一个字段:
{ name: "Asheesh" } // 合法
非常适用于:
- 更新 API;
- patch 请求;
- 表单编辑。
3)Pick<T, Keys> —— 只选择你需要的部分
type User = {
id: number;
name: string;
email: string;
};
type UserPreview = Pick<User, "name" | "email">;
适用于以下情况:
- 在 UI 中展示有限的数据
- 避免过度获取数据
- 创建轻量对象
4)Omit<T, Keys> —— 移除你不需要的部分
type User = {
id: number;
name: string;
email: string;
};
type UserWithoutId = Omit<User, "id">;
适用于以下情况:
- 移除敏感数据;
- 公共 API 响应;
- 仅用于 UI 的数据模型。
常见的 TypeScript“陷阱”
这些问题甚至会让有经验的开发者感到困惑。
1)“空对象”陷阱
很多人会这样写:
const obj: {} = {};
他们认为这表示“空对象”。
但它实际上表示:
任何非 null 或 undefined 的值
所以这是合法的 😳
const obj: {} = "hello";
const obj2: {} = 42;
这很危险。
正确的写法:
const obj: Record<string, never> = {};
现在它真正表示:
- 不允许任何属性
- 严格的空对象
2)可选链的陷阱
当你使用 ?. 时,TypeScript 会引入 undefined。
const name = user?.name;
类型会变成:
string | undefined
因此下面的写法会报错:
name.toUpperCase(); // ❌ 可能是 undefined
你必须进行处理:
if (name) {
name.toUpperCase(); // ✅ 安全
}
或者:
const safeName = name ?? "Guest";
工具类型就像 LEGO 工具。
你不需要每次都构建一个全新的玩具。你可以:
- 移除部分;
- 添加部分;
- 重塑结构。
而 TypeScript 已经为你提供了这些工具,你只需要使用它们。
为什么这在实际项目中很重要
这些工具可以帮助你:
- 避免重写类型;
- 保持 API 类型清晰;
- 减少重复;
- 让大型应用更易于管理。
结语
TypeScript 在一开始可能会让人感觉严格、困惑,甚至令人沮丧。我也经历过。但一旦你理解了一些核心模式,它就不再像是一个阻碍,而开始成为一个真正保护并引导你代码的工具。
这五个技巧表面上看很简单,但在真实项目中会带来巨大的变化:
- 更清晰的逻辑;
- 更少的 bug;
- 以及在编写代码时更强的信心。
以上关于每位前端开发者都应该掌握的 5 个 TypeScript 技巧的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 每位前端开发者都应该掌握的 5 个 TypeScript 技巧
微信
支付宝