TypeScript 20条实用技巧:工具类型、类型收窄、泛型与模板字面量
TypeScript早已成为日常开发的标配。本文整理了20条实用技巧,涵盖工具类型、类型收窄、泛型、枚举替代、satisfies、模板字面量类型、可辨识联合等,每条配有简洁代码示例,可直接用于项目。
一、工具类型(Utility Types)
1. Partial —— 所有属性可选
interface User {
id: number
name: string
email: string
}
// 更新时只需传部分字段
function updateUser(id: number, data: Partial<User>) {
// data 可以是 { name?: string, email?: string } 等
}
updateUser(1, { name: '张三' }) // 只更新name2. Required —— 所有属性必选
interface Config {
apiUrl?: string
timeout?: number
}
const config: Required<Config> = {
apiUrl: 'https://api.example.com',
timeout: 5000
}
// 缺少任意属性都会报错3. Pick —— 挑选部分属性
interface User {
id: number
name: string
email: string
password: string
}
type UserPreview = Pick<User, 'id' | 'name'>
// { id: number; name: string }4. Omit —— 排除部分属性
type UserWithoutPassword = Omit<User, 'password'>
// { id: number; name: string; email: string }5. Record —— 构造键值对类型
type Page = 'home' | 'about' | 'contact'
type PageConfig = Record<Page, { title: string; path: string }>
const config: PageConfig = {
home: { title: '首页', path: '/' },
about: { title: '关于', path: '/about' },
contact: { title: '联系', path: '/contact' }
}二、类型收窄(Type Narrowing)
6. typeof 收窄
function padLeft(value: string | number) {
if (typeof value === 'number') {
return value.toString().padStart(2, '0')
}
return value.padStart(2, '0') // 此处value已收窄为string
}7. 真值收窄
function print(str: string | null | undefined) {
if (str) {
console.log(str.toUpperCase()) // str已排除null/undefined
}
}8. in 操作符收窄
type Dog = { bark: () => void }
type Cat = { meow: () => void }
function speak(animal: Dog | Cat) {
if ('bark' in animal) {
animal.bark()
} else {
animal.meow()
}
}9. 类型谓词 is
function isString(val: unknown): val is string {
return typeof val === 'string'
}
const data: unknown = 'hello'
if (isString(data)) {
console.log(data.toUpperCase()) // data被收窄为string
}三、泛型在组件中的使用
10. 泛型组件(Vue 3)
<script setup lang="ts" generic="T">
defineProps<{
items: T[]
keyFn: (item: T) => string | number
}>()
</script>
<template>
<div v-for="item in items" :key="keyFn(item)">{{ item }}</div>
</template>11. 泛型约束 extends
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user = { name: '张三', age: 18 }
getProperty(user, 'name') // string
getProperty(user, 'age') // number
getProperty(user, 'foo') // 错误12. 泛型默认值
interface ApiResponse<T = unknown> {
code: number
data: T
}
// 不传泛型时,data为unknown
const res: ApiResponse = { code: 0, data: {} }
// 传泛型时
const userRes: ApiResponse<User> = { code: 0, data: { id: 1, name: '张三' } }四、枚举替代方案
13. 常量对象 + as const
const Status = {
Pending: 'pending',
Success: 'success',
Error: 'error'
} as const
type Status = (typeof Status)[keyof typeof Status]
// 'pending' | 'success' | 'error'14. 联合类型替代数字枚举
// 避免enum的额外运行时代码
type Direction = 'up' | 'down' | 'left' | 'right'五、satisfies 操作符(TS 4.9+)
15. 保持字面量类型同时满足接口
interface Colors {
primary: string
secondary: string
}
const theme = {
primary: '#3b82f6',
secondary: '#10b981'
} satisfies Colors
// theme.primary 类型为 '#3b82f6',而非string
// 同时满足Colors接口约束六、模板字面量类型
16. 字符串模板类型
type EventName = 'click' | 'focus' | 'blur'
type HandlerName = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'17. 动态路由类型
type Route = `/${string}`
type ApiRoute = `/api/${string}`
const path: Route = '/users'
const apiPath: ApiRoute = '/api/users'七、可辨识联合(Discriminated Unions)
18. 用 type 字段区分联合类型
type Success = { type: 'success'; data: string }
type Error = { type: 'error'; message: string }
type Result = Success | Error
function handle(result: Result) {
switch (result.type) {
case 'success':
console.log(result.data) // 自动收窄
break
case 'error':
console.log(result.message)
break
}
}八、其他实用技巧
19. 非空断言与可选链
const el = document.getElementById('app')!
el.innerHTML = 'hello'
// 可选链避免undefined
const name = user?.profile?.name ?? '匿名'20. 函数重载
function createElement(tag: 'div'): HTMLDivElement
function createElement(tag: 'span'): HTMLSpanElement
function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}
const div = createElement('div') // HTMLDivElement
const span = createElement('span') // HTMLSpanElement总结
| 技巧 | 适用场景 |
|---|---|
| Partial / Pick / Omit | 接口部分更新、类型复用 |
| Record | 映射类型、配置对象 |
| 类型收窄 | 联合类型分支处理 |
| 泛型组件 | 可复用的列表、表单组件 |
| satisfies | 保持字面量 + 类型检查 |
| 可辨识联合 | 状态机、API响应 |
| 常量对象 as const | 替代enum |
掌握这20条,日常TypeScript开发会顺畅很多,建议收藏。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!