TypeScript 的核心优势在于静态类型系统,而类型工具则是这个系统的“瑞士军刀”——它们帮助我们创建更灵活的类型、保障代码的类型安全,甚至实现复杂的“类型体操”。本文将从「类型创建」和「类型安全」两大维度,结合实际项目场景系统讲解 TypeScript 的核心类型工具,帮你真正把类型工具用在刀刃上。
一、类型创建工具:从封装到动态生成
类型创建工具的核心是基于已有类型生成新类型,解决类型复用和动态性问题。它们就像“类型层面的函数”,让你用更少的代码描述更复杂的类型结构。
1. 类型别名(Type Alias):封装与复用的基础
类型别名通过type关键字将一组类型或结构封装为一个新名称,是 TypeScript 最基础的类型工具。它的核心价值是减少重复代码,让类型定义更语义化。
基础用法:封装重复的业务类型
在实际项目中,我们经常需要重复使用某些类型(比如 API 响应、表单数据、状态机状态),用类型别名封装后,代码会更简洁、易维护。
示例 1:API 响应类型
API 接口的响应通常包含code(状态码)、message(提示信息)和data(实际数据),用类型别名封装后,所有 API 响应都能复用这个结构:
// 封装API响应的通用结构
type APIResponse<T> = {
code: number // 状态码(200=成功,4xx=客户端错误,5xx=服务端错误)
message: string // 提示信息
data: T // 实际数据(泛型,支持不同类型的响应数据)
}
// 具体API响应类型:用户列表
type UserListResponse = APIResponse<User[]>
// 具体API响应类型:用户详情
type UserDetailResponse = APIResponse<User>
// 用户类型(复用)
type User = {
id: number
name: string
email: string
createdAt: string // 注册时间(ISO字符串)
}示例 2:状态机状态
在状态机(比如订单状态、表单状态)中,用类型别名封装所有可能的状态,避免魔法字符串:
// 订单状态(可辨识联合类型的基础,后面会详细讲)
type OrderStatus = 'pending' | 'paid' | 'shipped' | 'canceled'进阶:工具类型(泛型+类型别名)
工具类型是带泛型的类型别名,相当于“类型层面的函数”——接受泛型参数,生成动态类型。TypeScript 内置了很多常用工具类型(如Partial、Required、Omit),但我们也可以自定义。
常用工具类型实战
Maybe<T>:允许值为null/undefined
实际项目中,很多字段是可选的(比如用户的生日、地址),用Maybe封装后,类型更清晰:tstype Maybe<T> = T | null | undefined // 用户类型:生日和地址是可选的 type User = { id: number name: string email: string birthday: Maybe<string> // 生日可能为null/undefined address: Maybe<string> // 地址可能为null/undefined }Partial<T>:将对象属性转为可选
表单数据在初始化时通常是“部分填写”的,用Partial将User类型的所有属性转为可选:ts// 表单初始数据:所有字段可选 type UserFormData = Partial<User> const initialFormData: UserFormData = { name: '', email: '' } // 生日和地址默认未填写Required<T>:将对象属性转为必填
当表单提交时,所有字段必须填写,用Required将UserFormData转为必填:ts// 提交时的表单数据:所有字段必填 type RequiredUserFormData = Required<UserFormData> function submitForm(data: RequiredUserFormData) { /* 提交逻辑 */ }Omit<T, K>:从对象中排除指定属性
展示用户信息时,需要隐藏敏感字段(如id、createdAt),用Omit排除这些字段:ts// 用户信息展示类型:排除敏感字段 type UserDisplay = Omit<User, 'id' | 'createdAt'> // UserDisplay等价于:{ name: string; email: string; birthday: Maybe<string>; address: Maybe<string> }
2. 联合类型与交叉类型:组合类型的两种方式
联合类型(|)和交叉类型(&)是组合多个类型的核心工具,但语义完全不同:
联合类型(|):满足任一类型即可(逻辑或)
联合类型用于描述“一个值可能是多种类型中的一种”,常见于API 响应的不同状态、用户输入的多种形式等场景。
实战示例:API 响应的成功/失败状态
API 接口的响应通常有两种状态:成功(返回数据)或失败(返回错误信息),用联合类型描述后,TypeScript 能自动收窄类型:
// 成功响应:status=success,包含数据
type SuccessResponse<T> = {
status: 'success'
data: T
}
// 失败响应:status=error,包含错误信息
type ErrorResponse = {
status: 'error'
message: string
code?: number // 可选错误码
}
// API响应的联合类型
type APIResponse<T> = SuccessResponse<T> | ErrorResponse
// 使用:处理API响应
async function fetchUserList(): Promise<APIResponse<User[]>> {
const res = await fetch('/api/users')
if (!res.ok) {
return { status: 'error', message: '获取用户列表失败' }
}
const data = await res.json()
return { status: 'success', data }
}
// 调用函数并处理响应
fetchUserList().then((response) => {
if (response.status === 'success') {
// TypeScript自动收窄为SuccessResponse<User[]>
console.log('用户列表:', response.data)
} else {
// TypeScript自动收窄为ErrorResponse
console.error('错误:', response.message)
}
})交叉类型(&):满足所有类型(逻辑与)
交叉类型用于描述“一个值同时具备多种类型的特征”,常见于合并多个对象的属性(如用户信息+权限信息)、取类型的交集等场景。
实战示例:用户信息+权限信息
在后台管理系统中,用户不仅有基本信息,还有权限信息(角色、可操作的资源),用交叉类型合并后,类型更完整:
// 用户基本信息
type User = {
id: number
name: string
email: string
}
// 用户权限信息
type Permissions = {
role: 'admin' | 'editor' | 'viewer' // 角色(管理员/编辑/查看者)
canEdit: boolean // 是否可编辑
canDelete: boolean // 是否可删除
}
// 合并后的用户类型:同时具备基本信息和权限信息
type UserWithPermissions = User & Permissions
// 使用:获取用户信息(包含权限)
async function fetchUserWithPermissions(
id: number
): Promise<UserWithPermissions> {
const user = await fetch(`/api/users/${id}`).then((res) => res.json())
const permissions = await fetch(`/api/users/${id}/permissions`).then((res) =>
res.json()
)
return { ...user, ...permissions } // 合并两个对象
}3. 索引类型:访问与操作对象的键值
索引类型包含三个核心工具:索引签名、索引查询(keyof)、索引访问(T[K])。它们的核心是通过“键”来操作对象的类型,常见于处理动态对象(如配置、字典)。
索引签名:声明任意属性的类型
当对象的键是动态的(比如环境变量、配置对象),用索引签名描述“所有键的类型”和“对应值的类型”。
实战示例:环境变量配置
在 Node.js 项目中,环境变量(process.env)是动态的,用索引签名封装后,类型更安全:
// 环境变量配置(键为字符串,值为字符串或数字)
type EnvConfig = {
[key: string]: string | number
}
// 加载环境变量(示例)
const env: EnvConfig = {
API_URL: process.env.API_URL || 'https://api.example.com',
PORT: Number(process.env.PORT) || 3000,
JWT_SECRET: process.env.JWT_SECRET || 'my-secret-key',
}索引查询(keyof):获取对象的键名联合类型
keyof将对象的键名转为字符串/数字字面量联合类型,常用于生成“键的集合”(比如下拉选项的 value)。
实战示例:生成下拉选项的类型
在用户列表页面,我们需要一个下拉菜单来选择“排序字段”(如id、name、createdAt),用keyof获取User的所有键:
// User类型(复用)
type User = {
id: number
name: string
email: string
createdAt: string
}
// 获取User的所有键(字面量联合类型)
type UserSortField = keyof User // "id" | "name" | "email" | "createdAt"
// 下拉选项的类型(value为排序字段,label为显示文本)
type SortOption = {
value: UserSortField
label: string
}
// 排序选项列表(类型安全:value必须是User的键)
const sortOptions: SortOption[] = [
{ value: 'id', label: '按ID排序' },
{ value: 'name', label: '按姓名排序' },
{ value: 'createdAt', label: '按注册时间排序' },
]索引访问(T[K]):获取对象的键值类型
通过“键名字面量”访问对象的键值类型,常用于获取嵌套对象的类型或动态字段的类型。
实战示例:获取 API 响应中的数据字段类型
在 API 响应中,data字段的类型是动态的,用索引访问获取具体字段的类型:
// API响应类型(复用前面的定义)
type APIResponse<T> = {
code: number
message: string
data: T
}
// 用户列表响应类型
type UserListResponse = APIResponse<User[]>
// 获取data字段的类型(User[])
type UserListData = UserListResponse['data'] // User[]
// 获取data字段中元素的类型(User)
type UserListItem = UserListData[number] // User(数组元素的类型)4. 映射类型:遍历键生成新类型
映射类型通过in关键字遍历联合类型的每一个成员,生成新的对象类型。它是实现复杂工具类型的核心,常见于修改对象的属性(如可选、只读)或筛选属性。
基础:克隆对象类型
映射类型的最基础用法是克隆一个对象类型(等价于原类型):
type Clone<T> = {
[K in keyof T]: T[K] // 遍历T的所有键,复制对应的键值类型
}
type ClonedUser = Clone<User> // 和User完全一致实战:实现常用工具类型
示例 1:Pick<T, K>:从对象中选取指定属性
在用户信息展示页面,我们只需要name、email、createdAt三个字段,用Pick筛选:
// Pick工具类型(TypeScript内置,这里手动实现)
type Pick<T, K extends keyof T> = {
[P in K]: T[P] // 遍历K中的每一个键,取T对应的值类型
}
// 选取User中的指定字段(展示用)
type UserDisplay = Pick<User, 'name' | 'email' | 'createdAt'>
// UserDisplay等价于:{ name: string; email: string; createdAt: string }示例 2:Omit<T, K>:从对象中排除指定属性
在用户编辑页面,我们需要排除id(不可修改)和createdAt(不可修改)字段,用Omit实现:
// Omit工具类型(TypeScript内置,这里手动实现)
type Omit<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P] // 遍历T中不在K里的键
}
// 排除User中的敏感字段(编辑用)
type UserEdit = Omit<User, 'id' | 'createdAt'>
// UserEdit等价于:{ name: string; email: string }示例 3:Readonly<T>:将对象转为只读
在用户信息展示页面,所有字段不可修改,用Readonly将对象转为只读:
// Readonly工具类型(TypeScript内置,这里手动实现)
type Readonly<T> = {
readonly [K in keyof T]: T[K] // 给每个键加readonly修饰符
}
// 只读用户类型(展示用)
type ReadonlyUser = Readonly<User>
// 使用:展示用户信息(不可修改)
const user: ReadonlyUser = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
createdAt: '2024-01-01',
}
user.name = 'Bob' // 报错:readonly属性不可修改二、类型安全工具:保障代码的类型正确性
类型安全工具的核心是让 TypeScript 理解代码的逻辑,自动收窄类型,避免类型错误。它们就像“类型层面的守卫”,确保你的代码在运行时不会出现undefined、null或类型不匹配的错误。
1. 类型查询(typeof):从变量获取类型
TypeScript 的typeof有两种用法:
- JavaScript 中的
typeof:运行时判断变量类型(返回字符串,如"string"、"number"); - TypeScript 中的
typeof:编译时从变量获取类型(返回 TypeScript 类型,如string、number)。
实战示例:获取函数返回值类型
在 API 调用中,我们常常用函数封装请求逻辑,用typeof获取函数返回值类型,避免重复定义:
// 封装API请求函数
async function fetchUsers(): Promise<User[]> {
const res = await fetch('/api/users')
return res.json()
}
// 获取函数返回值类型(User[])
type UserList = Awaited<ReturnType<typeof fetchUsers>> // Awaited处理Promise
// 使用:定义用户列表的状态(React/Vue中的状态类型)
type UserListState = {
loading: boolean
data: UserList | null // 初始为null
error: string | null // 错误信息
}2. 类型守卫:收窄类型的“开关”
类型守卫通过逻辑判断告诉 TypeScript:“这个变量在这个分支中一定是某个类型!”常见的类型守卫有typeof、in、instanceof和自定义守卫。
(1)typeof类型守卫:判断原始类型
typeof是最常用的类型守卫,用于判断原始类型(string、number、boolean、undefined、symbol)。
实战示例:处理用户输入的多种类型
在搜索功能中,用户可能输入string(用户名)或number(用户 ID),用typeof收窄类型:
// 搜索函数:支持用户名(string)或用户ID(number)
function searchUser(input: string | number) {
if (typeof input === 'string') {
// 输入是字符串:按用户名搜索
return fetch(`/api/users?name=${input}`)
} else {
// 输入是数字:按用户ID搜索
return fetch(`/api/users/${input}`)
}
}(2)in类型守卫:判断对象是否有某个属性
in操作符是 JavaScript 的原生语法,用于判断对象是否包含某个属性(包括原型链上的属性)。
在 TypeScript 中,in可以作为类型守卫,帮助我们区分联合类型中的不同结构。
实战示例:处理 API 响应的成功/失败
回到之前的APIResponse联合类型,我们可以用in判断响应是否包含data字段(成功响应)或message字段(失败响应):
// API响应类型(复用前面的定义)
type SuccessResponse<T> = { status: 'success'; data: T }
type ErrorResponse = { status: 'error'; message: string }
type APIResponse<T> = SuccessResponse<T> | ErrorResponse // 处理API响应的函数
function handleAPIResponse<T>(response: APIResponse<T>) {
if ('data' in response) {
// TypeScript自动收窄为SuccessResponse<T>:包含data字段
console.log('请求成功,数据:', response.data)
} else {
// TypeScript自动收窄为ErrorResponse:包含message字段
console.error('请求失败,原因:', response.message)
}
} // 使用:处理用户列表响应
const userListResponse: APIResponse<User[]> = {
status: 'success',
data: [{ id: 1, name: 'Alice' }],
}
handleAPIResponse(userListResponse) // 输出:请求成功,数据:[...]// 处理失败响应
const errorResponse: APIResponse<User[]> = {
status: 'error',
message: '服务器内部错误',
}
handleAPIResponse(errorResponse) // 输出:请求失败,原因:服务器内部错误(3)instanceof类型守卫:判断类实例
instanceof是 JavaScript 的原生语法,用于判断对象是否是某个类的实例(检查原型链)。在 TypeScript 中,instanceof可以作为类型守卫,帮助我们区分不同类的实例。
实战示例:处理不同的支付方式
在电商项目中,支付方式可能有多种(比如支付宝、微信支付),每种支付方式有不同的处理逻辑。用instanceof可以轻松区分:
// 抽象支付类
abstract class Payment {
abstract pay(amount: number): Promise<void> // 抽象方法:支付
} // 支付宝支付类
class Alipay extends Payment {
async pay(amount: number) {
console.log(`用支付宝支付了 ${amount} 元`)
}
} // 微信支付类
class WeChatPay extends Payment {
async pay(amount: number) {
console.log(`用微信支付了 ${amount} 元`)
}
} // 处理支付的函数
async function processPayment(payment: Payment, amount: number) {
if (payment instanceof Alipay) {
// TypeScript自动收窄为Alipay实例:可以调用Alipay的专有方法(如果有的话)
await payment.pay(amount)
} else if (payment instanceof WeChatPay) {
// TypeScript自动收窄为WeChatPay实例
await payment.pay(amount)
} else {
throw new Error('不支持的支付方式')
}
} // 使用:创建支付宝支付实例并处理
const alipay = new Alipay()
processPayment(alipay, 100) // 输出:用支付宝支付了 100 元// 使用:创建微信支付实例并处理
const wechatPay = new WeChatPay()
processPayment(wechatPay, 200) // 输出:用微信支付了 200 元(4)自定义类型守卫(is关键字)
当typeof、in、instanceof无法满足需求时,我们可以用is关键字自定义类型守卫。自定义类型守卫的核心是通过逻辑判断返回一个布尔值,并告诉 TypeScript“如果返回true,则变量是某个类型”。
实战示例 1:判断是否为函数
在处理回调函数时,我们需要确保输入是一个函数,否则抛出错误。用自定义类型守卫实现:
// 自定义类型守卫:判断是否为函数
function isFunction<T extends (...args: any[]) => any>(
input: unknown
): input is T {
return typeof input === 'function'
} // 使用:处理回调函数
function executeCallback(callback: unknown) {
if (isFunction(callback)) {
// TypeScript自动收窄为函数类型:可以安全调用
callback()
} else {
throw new Error('回调函数必须是一个函数')
}
} // 测试:传入函数
executeCallback(() => console.log('回调执行了')) // 输出:回调执行了// 测试:传入非函数
executeCallback('not a function') // 抛出错误:回调函数必须是一个函数实战示例 2:判断是否为日期对象
在处理日期字符串或日期对象时,我们需要区分输入类型,用自定义类型守卫实现:
// 自定义类型守卫:判断是否为日期对象
function isDate(input: unknown): input is Date {
return input instanceof Date && !isNaN(input.getTime())
} // 使用:格式化日期
function formatDate(input: string | Date): string {
if (isDate(input)) {
// TypeScript自动收窄为Date类型:可以调用Date的方法
return input.toLocaleDateString()
} else {
// 输入是字符串:尝试解析为日期
const date = new Date(input)
if (isDate(date)) {
return date.toLocaleDateString()
}
throw new Error('无效的日期格式')
}
} // 测试:传入日期对象
formatDate(new Date('2024-01-01')) // 输出:2024/1/1(根据本地化设置)// 测试:传入日期字符串
formatDate('2024-02-14') // 输出:2024/2/14// 测试:传入无效字符串
formatDate('invalid date') // 抛出错误:无效的日期格式2. 类型断言守卫(asserts):断言不成立则抛出错误
类型断言守卫(Assertion Guards)是 TypeScript 3.7 引入的特性,用于强制保证类型——如果断言不成立,则抛出错误。它的核心是asserts关键字,告诉 TypeScript“如果这个函数返回,则断言的条件一定成立”。
实战示例:处理分页参数
在分页查询中,page(当前页)和limit(每页数量)必须是大于 0 的数字。用类型断言守卫强制保证这一点:
// 自定义断言守卫:保证输入是大于0的数字
function assertIsPositiveNumber(
input: unknown,
name: string
): asserts input is number {
if (typeof input !== 'number' || input <= 0) {
throw new Error(`${name}必须是大于0的数字,当前值:${input}`)
}
} // 使用:处理分页参数
function getPaginatedData(page: unknown, limit: unknown) {
// 强制保证page和limit是大于0的数字
assertIsPositiveNumber(page, 'page')
assertIsPositiveNumber(limit, 'limit') // 此时page和limit的类型是number,可以安全使用
console.log(`获取第 ${page} 页,每页 ${limit} 条数据`)
// 实际逻辑:调用API获取数据,如 fetch(`/api/data?page=${page}&limit=${limit}`)
} // 测试:传入有效参数
getPaginatedData(2, 10) // 输出:获取第 2 页,每页 10 条数据// 测试:传入无效参数(page为字符串)
getPaginatedData('3', 10) // 抛出错误:page必须是大于0的数字,当前值:3// 测试:传入无效参数(limit为0)
getPaginatedData(2, 0) // 抛出错误:limit必须是大于0的数字,当前值:0为什么用类型断言守卫?
相比普通的类型守卫,类型断言守卫更“严格”——它会强制终止程序(抛出错误)如果类型不匹配,而不是仅仅收窄类型。这在处理用户输入、配置参数或第三方 API 返回值时非常有用,确保这些关键参数符合预期,避免后续逻辑出现不可控的错误。
三、扩展阅读:进阶类型工具与最佳实践
掌握了基础类型工具后,我们可以探索更进阶的用法,比如可辨识联合类型、条件类型、递归类型等,这些工具能帮你处理更复杂的类型场景。
1. 可辨识联合类型:更智能的类型收窄
可辨识联合类型(Discriminated Unions)是带“标签”的联合类型,通过一个共同的“标签属性”(比如type、kind、status)来区分联合类型中的不同成员。TypeScript 能自动根据标签属性收窄类型,无需额外的类型守卫。
实战示例:处理订单状态
在电商项目中,订单有不同的状态(pending、paid、shipped、canceled),每个状态有不同的属性(比如paid状态有paidAt,shipped状态有shippedAt):
// 可辨识联合类型:订单状态
type Order =
| { status: 'pending'; id: number; createdAt: string } // 待支付:包含创建时间
| { status: 'paid'; id: number; paidAt: string } // 已支付:包含支付时间
| { status: 'shipped'; id: number; shippedAt: string } // 已发货:包含发货时间
| { status: 'canceled'; id: number; canceledAt: string } // 已取消:包含取消时间// 处理订单的函数
function handleOrder(order: Order) {
switch (order.status) {
case 'pending':
// TypeScript自动收窄为pending状态:可以访问createdAt
console.log(`订单 ${order.id} 待支付,创建于 ${order.createdAt}`)
break
case 'paid':
// TypeScript自动收窄为paid状态:可以访问paidAt
console.log(`订单 ${order.id} 已支付,支付于 ${order.paidAt}`)
break
case 'shipped':
// TypeScript自动收窄为shipped状态:可以访问shippedAt
console.log(`订单 ${order.id} 已发货,发货于 ${order.shippedAt}`)
break
case 'canceled':
// TypeScript自动收窄为canceled状态:可以访问canceledAt
console.log(`订单 ${order.id} 已取消,取消于 ${order.canceledAt}`)
break
default:
// never类型:覆盖所有可能的状态,避免遗漏
const _exhaustiveCheck: never = order
throw new Error(`未知的订单状态:${order.status}`)
}
}为什么用可辨识联合类型?
可辨识联合类型让类型收窄更“智能”——只需检查标签属性,TypeScript 就能自动推断出对应的类型,无需额外的in或typeof检查。这在处理状态机、策略模式等场景时非常高效,代码也更简洁。
2. 条件类型:动态选择类型
条件类型(Conditional Types)是带条件判断的类型,语法是T extends U ? X : Y(如果 T 是 U 的子类型,则返回 X,否则返回 Y)。它的核心是根据泛型参数动态选择类型,常见于实现复杂的工具类型(如Exclude、Extract、ReturnType)。
实战示例:实现ReturnType工具类型ReturnType<T>用于获取函数的返回值类型,TypeScript 内置了这个工具类型,但我们可以手动实现来理解其原理:
// 条件类型:如果T是函数,则返回其返回值类型,否则返回never
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never // 使用:获取函数返回值类型
function add(a: number, b: number) {
return a + b
}
type AddReturnType = ReturnType<typeof add> // numberfunction greet(name: string) { return `Hello, ${name}!`; }
type GreetReturnType = ReturnType<typeof greet> // string解释:
T extends (...args: any[]) => infer R:检查 T 是否是函数类型,infer R表示“推断函数的返回值类型为 R”;? R : never:如果是函数类型,则返回 R(返回值类型),否则返回 never(不存在的类型)。
四、总结:类型工具的应用场景与最佳实践
TypeScript 的类型工具非常丰富,但核心是“用类型描述逻辑”——让 TypeScript 帮你提前发现错误,而不是等到运行时才崩溃。以下是最佳实践:
1. 优先用类型别名封装重复类型
- 对于重复使用的业务类型(如 API 响应、表单数据、状态机状态),用类型别名封装,减少重复代码;
- 对于工具类型(如
Maybe、Partial、Omit),用泛型+类型别名实现,提高复用性。
2. 用联合类型和交叉类型组合复杂类型
- 用联合类型表示“多种可能的类型”(如 API 响应的成功/失败、状态机的状态);
- 用交叉类型表示“同时具备多种类型的特征”(如用户信息+权限信息、对象的合并)。
3. 用类型守卫保障类型安全
- 对于原始类型,用
typeof; - 对于对象的属性,用
in; - 对于类实例,用
instanceof; - 对于自定义场景,用
is关键字的自定义类型守卫; - 对于强制保证类型的场景,用
asserts关键字的类型断言守卫。
4. 用可辨识联合类型处理状态机
- 对于状态机、策略模式等场景,用可辨识联合类型(带标签属性),让 TypeScript 自动收窄类型,避免遗漏状态。
5. 避免过度使用类型工具
- 类型工具是为了提高代码的可维护性,而不是为了“炫技”;
- 对于简单的类型,直接写原始类型即可,无需封装为类型别名;
- 对于复杂的类型,优先用内置工具类型(如
Partial、Omit),避免重复造轮子。
下一步:挑战更复杂的类型体操
掌握了上述核心工具后,你可以尝试挑战更复杂的类型体操,比如:
DeepPartial<T>:深度可选类型(将对象的所有嵌套属性转为可选);RequiredDeep<T>:深度必填类型(将对象的所有嵌套属性转为必填);PickByType<T, U>:根据类型筛选属性(选取值类型为 U 的属性);OmitByType<T, U>:根据类型排除属性(排除值类型为 U 的属性)。这些类型体操能帮你更深入理解 TypeScript 的类型系统,但记住:类型体操是手段,不是目的——最终目标是写出更安全、更可维护的代码。
参考文档:TypeScript 官方文档 到这里,TypeScript 的核心类型工具就讲解完毕了。希望这篇文章能帮你从“会用 TypeScript”到“用好 TypeScript”,写出更安全、更优雅的代码! 🚀
- 本文链接:https://fridolph.top/posts/2025-05-03__ts01
- 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 许可协议。