高级特性
本章介绍 TypeScript 高级类型特性的使用方法和注意事项。
高级类型特性
1.1 工具类型使用
1.1.1 充分利用内置工具类型
typescript
// ✅ 使用内置工具类型而不是自己实现
// Partial - 将所有属性变为可选
interface User {
id: string
name: string
email: string
age: number
}
type UpdateUser = Partial<User> // 所有字段可选,用于更新
// Pick - 选择部分属性
type UserPreview = Pick<User, 'id' | 'name'> // 只包含 id 和 name
// Omit - 排除部分属性
type UserWithoutEmail = Omit<User, 'email'> // 排除 email 字段
// Required - 将所有属性变为必需
type CompleteUser = Required<Partial<User>> // 所有字段必需
// Readonly - 将所有属性变为只读
type ImmutableUser = Readonly<User> // 所有字段只读
// Record - 构造对象类型
type UserMap = Record<string, User> // { [key: string]: User }
type Roles = Record<'admin' | 'user' | 'guest', string[]>
// Extract - 提取联合类型中的特定类型
type Status = 'idle' | 'loading' | 'success' | 'error'
type FinishedStatus = Extract<Status, 'success' | 'error'> // 'success' | 'error'
// Exclude - 排除联合类型中的特定类型
type PendingStatus = Exclude<Status, FinishedStatus> // 'idle' | 'loading'
// ReturnType - 获取函数返回类型
function getUser() {
return { id: '1', name: 'John' }
}
type UserType = ReturnType<typeof getUser>
// Parameters - 获取函数参数类型
function createUser(name: string, age: number) { }
type CreateUserParams = Parameters<typeof createUser> // [string, number]
// Awaited - 获取 Promise 解析后的类型 (TS 4.5+)
type PromiseUser = Promise<User>
type ResolvedUser = Awaited<PromiseUser> // User
1.1.2 组合使用工具类型
typescript
// 组合多个工具类型创建复杂类型
interface Article {
id: string
title: string
content: string
author: User
tags: string[]
publishedAt: Date
updatedAt: Date
viewCount: number
draft: boolean
}
// 创建文章的输入类型(排除自动生成的字段)
type CreateArticleInput = Omit<Article,
'id' | 'publishedAt' | 'updatedAt' | 'viewCount'
>
// 更新文章的输入类型(所有字段可选,除了 id)
type UpdateArticleInput = Partial<
Omit<Article, 'id' | 'publishedAt'>
> & { id: string }
// 文章预览类型
type ArticlePreview = Pick<Article,
'id' | 'title' | 'author' | 'publishedAt' | 'tags'
> & {
contentPreview: string // 添加额外字段
}
// 只读的已发布文章
type PublishedArticle = Readonly<
Omit<Article, 'draft'>
> & { draft: false }
1.1.3 自定义工具类型
typescript
// 当内置工具类型不满足需求时,创建自定义工具类型
// DeepPartial - 深度可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P]>
: T[P]
}
// DeepReadonly - 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P]
}
// Nullable - 添加 null 类型
type Nullable<T> = T | null
// NonNullableKeys - 获取非空属性的键
type NonNullableKeys<T> = {
[K in keyof T]: T[K] extends null | undefined ? never : K
}[keyof T]
// Mutable - 移除 readonly
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}
// PickByType - 根据值类型选择属性
type PickByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P]
}
// 使用示例
interface Config {
apiUrl: string
timeout: number
retryCount: number
enableCache: boolean
enableLog: boolean
}
type NumberConfig = PickByType<Config, number>
// { timeout: number; retryCount: number }
type BooleanConfig = PickByType<Config, boolean>
// { enableCache: boolean; enableLog: boolean }
1.2 条件类型封装
1.2.1 封装复杂条件类型
typescript
// ✅ 将复杂的条件类型封装为可复用的工具类型
// IsArray - 判断是否为数组
type IsArray<T> = T extends any[] ? true : false
// ArrayElement - 获取数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never
// UnwrapPromise - 解包 Promise
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
// FunctionArgs - 获取函数参数
type FunctionArgs<T> = T extends (...args: infer A) => any ? A : never
// Flatten - 扁平化类型
type Flatten<T> = T extends any[]
? T[number]
: T extends object
? T[keyof T]
: T
// 使用示例
type IsUserArray = IsArray<User[]> // true
type IsUser = IsArray<User> // false
type UserElement = ArrayElement<User[]> // User
type PromiseContent = UnwrapPromise<Promise<User>> // User
type NormalContent = UnwrapPromise<User> // User
function fetchUsers(...ids: string[]): Promise<User[]> { }
type FetchUsersArgs = FunctionArgs<typeof fetchUsers> // [string[]]
1.2.2 条件类型与映射类型结合
typescript
// 根据条件修改对象类型
// 将可选属性变为必需
type RequiredByKeys<T, K extends keyof T> =
Omit<T, K> & Required<Pick<T, K>>
// 将特定属性变为可选
type OptionalByKeys<T, K extends keyof T> =
Omit<T, K> & Partial<Pick<T, K>>
// 根据条件修改属性类型
type NullableProperties<T, K extends keyof T> = {
[P in keyof T]: P extends K ? T[P] | null : T[P]
}
// 过滤出特定类型的属性
type FunctionProperties<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K]
}
// 使用示例
interface Product {
id: string
name: string
price?: number
description?: string
calculate: (tax: number) => number
format: () => string
}
// 将 price 变为必需
type ProductWithPrice = RequiredByKeys<Product, 'price'>
// 将 id 和 name 变为可选
type FlexibleProduct = OptionalByKeys<Product, 'id' | 'name'>
// 将 price 和 description 变为可 null
type NullableProduct = NullableProperties<Product, 'price' | 'description'>
// 只保留函数属性
type ProductMethods = FunctionProperties<Product>
// { calculate: (tax: number) => number; format: () => string }
1.2.3 递归条件类型
typescript
// TypeScript 4.1+ 支持递归条件类型
// DeepReplace - 深度替换类型
type DeepReplace<T, From, To> = T extends From
? To
: T extends object
? { [K in keyof T]: DeepReplace<T[K], From, To> }
: T
// Paths - 获取对象所有路径
type Paths<T> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? K | `${K}.${Paths<T[K]>}`
: K
: never
}[keyof T]
: never
// 使用示例
interface NestedConfig {
api: {
url: string
timeout: number | null
retry: {
count: number | null
delay: number | null
}
}
features: {
enableCache: boolean | null
}
}
// 将所有 null 替换为 undefined
type ConfigWithUndefined = DeepReplace<NestedConfig, null, undefined>
// 获取所有路径
type ConfigPaths = Paths<NestedConfig>
// "api" | "api.url" | "api.timeout" | "api.retry" | "api.retry.count" | "api.retry.delay" | "features" | "features.enableCache"
1.3 类型守卫实现
1.3.1 综合使用各种守卫方式
typescript
// ✅ 根据场景选择合适的类型守卫方式
// 1. 类型谓词(Type Predicates)- 适合复杂判断
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'email' in value
)
}
// 2. in 操作符 - 适合鸭子类型
interface Bird {
fly(): void
layEggs(): void
}
interface Fish {
swim(): void
layEggs(): void
}
function isBird(pet: Bird | Fish): pet is Bird {
return 'fly' in pet
}
// 3. instanceof - 适合类实例
class HttpError extends Error {
constructor(
message: string,
public statusCode: number
) {
super(message)
}
}
function isHttpError(error: unknown): error is HttpError {
return error instanceof HttpError
}
// 4. typeof - 适合基础类型
function isString(value: unknown): value is string {
return typeof value === 'string'
}
// 5. 自定义属性检查
interface Tagged {
_tag: string
}
interface SuccessResponse extends Tagged {
_tag: 'success'
data: unknown
}
interface ErrorResponse extends Tagged {
_tag: 'error'
message: string
}
type Response = SuccessResponse | ErrorResponse
function isSuccess(response: Response): response is SuccessResponse {
return response._tag === 'success'
}
1.3.2 组合类型守卫
typescript
// 创建可组合的类型守卫工具
// 基础守卫
const Guards = {
isString: (value: unknown): value is string =>
typeof value === 'string',
isNumber: (value: unknown): value is number =>
typeof value === 'number' && !isNaN(value),
isObject: (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value !== null && !Array.isArray(value),
isArray: <T>(
value: unknown,
itemGuard?: (item: unknown) => item is T
): value is T[] => {
if (!Array.isArray(value)) return false
if (!itemGuard) return true
return value.every(itemGuard)
},
hasProperty: <K extends string>(
value: unknown,
key: K
): value is Record<K, unknown> => {
return Guards.isObject(value) && key in value
}
}
// 组合守卫
function isUserArray(value: unknown): value is User[] {
return Guards.isArray(value, isUser)
}
function isStringArray(value: unknown): value is string[] {
return Guards.isArray(value, Guards.isString)
}
// 高级组合
function createGuard<T>(
checks: Array<(value: unknown) => boolean>
): (value: unknown) => value is T {
return (value: unknown): value is T => {
return checks.every(check => check(value))
}
}
// 使用示例
const isValidUser = createGuard<User>([
(v) => Guards.hasProperty(v, 'id'),
(v) => Guards.hasProperty(v, 'name'),
(v) => Guards.hasProperty(v, 'email'),
(v) => Guards.isString((v as any).id),
(v) => Guards.isString((v as any).name),
(v) => Guards.isString((v as any).email)
])
1.3.3 断言函数(Assertion Functions)
typescript
// TypeScript 3.7+ 支持断言函数
// 断言函数 - 如果不满足条件则抛出错误
function assertIsUser(value: unknown): asserts value is User {
if (!isUser(value)) {
throw new Error('Value is not a User')
}
}
function assertIsNotNull<T>(
value: T | null,
message = 'Value is null'
): asserts value is T {
if (value === null) {
throw new Error(message)
}
}
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`)
}
// 使用示例
function processUser(value: unknown) {
assertIsUser(value)
// 之后 value 的类型是 User
console.log(value.name)
console.log(value.email)
}
function handleStatus(status: Status) {
switch (status) {
case 'idle':
return 'Waiting'
case 'loading':
return 'Processing'
case 'success':
return 'Complete'
case 'error':
return 'Failed'
default:
assertNever(status) // 确保处理了所有情况
}
}
实验性特性
2.1 装饰器使用
2.1.1 等待 Stage 3 稳定
typescript
// ⚠️ 装饰器目前仍是实验性特性,等待 Stage 3 稳定后使用
// 当前状态(实验性)
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true, // 旧版装饰器
"emitDecoratorMetadata": true
}
}
// 未来 Stage 3 装饰器示例(语法可能变化)
// 类装饰器
function Component(target: any) {
// 装饰器逻辑
}
// 方法装饰器
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value
descriptor.value = function(...args: any[]) {
console.log(`Calling ${key} with args:`, args)
return original.apply(this, args)
}
}
// 属性装饰器
function Required(target: any, key: string) {
// 验证逻辑
}
2.1.2 框架特定的装饰器使用
typescript
// 某些框架(如 Angular、NestJS)强制要求装饰器
// NestJS 示例(框架要求时可以使用)
import { Controller, Get, Post, Body } from '@nestjs/common'
@Controller('users')
export class UserController {
@Get()
async findAll(): Promise<User[]> {
return this.userService.findAll()
}
@Post()
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.userService.create(createUserDto)
}
}
// Angular 示例
import { Component, Input, Output, EventEmitter } from '@angular/core'
@Component({
selector: 'app-user',
template: '<div>{{user.name}}</div>'
})
export class UserComponent {
@Input() user: User
@Output() userClick = new EventEmitter<User>()
}
2.1.3 替代方案
typescript
// 在装饰器稳定前,使用替代方案
// 1. 高阶函数替代类装饰器
function withLogging<T extends { new(...args: any[]): {} }>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args)
console.log('Instance created:', this)
}
}
}
class User {
constructor(public name: string) {}
}
const LoggedUser = withLogging(User)
const user = new LoggedUser('John')
// 2. 属性包装器替代属性装饰器
class Model {
private _id: string = ''
get id(): string {
console.log('Getting id:', this._id)
return this._id
}
set id(value: string) {
console.log('Setting id:', value)
this._id = value
}
}
// 3. 工厂函数替代方法装饰器
function createLoggingMethod<T extends (...args: any[]) => any>(
method: T,
name: string
): T {
return ((...args: Parameters<T>) => {
console.log(`Calling ${name} with args:`, args)
const result = method(...args)
console.log(`${name} returned:`, result)
return result
}) as T
}
class Service {
process = createLoggingMethod(
(data: string) => {
return data.toUpperCase()
},
'process'
)
}
2.2 其他实验性特性
2.2.1 模板字面量类型的高级用法
typescript
// TypeScript 4.1+ 的模板字面量类型
// 路由路径类型
type Route = '/users' | '/posts' | '/comments'
type ApiRoute = `api${Route}` // 'api/users' | 'api/posts' | 'api/comments'
// CSS 单位类型
type CSSUnit = 'px' | 'em' | 'rem' | '%'
type CSSValue = `${number}${CSSUnit}`
// 事件名类型
type EventName = 'click' | 'focus' | 'blur'
type EventHandler = `on${Capitalize<EventName>}` // 'onClick' | 'onFocus' | 'onBlur'
// 实用示例:创建 BEM 类名
type BEMBlock = 'button' | 'card' | 'modal'
type BEMElement = 'header' | 'body' | 'footer'
type BEMModifier = 'active' | 'disabled' | 'large'
type BEMClassName =
| BEMBlock
| `${BEMBlock}__${BEMElement}`
| `${BEMBlock}--${BEMModifier}`
| `${BEMBlock}__${BEMElement}--${BEMModifier}`
const className: BEMClassName = 'button__header--active' // 类型安全
2.2.2 Satisfies 操作符(TypeScript 4.9+)
typescript
// satisfies 操作符:验证类型但保留推断
// 不使用 satisfies(类型被扩宽)
const config1: Record<string, string | number> = {
api: 'https://api.example.com',
timeout: 5000
}
// config1.api 的类型是 string | number(不够精确)
// 使用 satisfies(保留精确类型)
const config2 = {
api: 'https://api.example.com',
timeout: 5000
} satisfies Record<string, string | number>
// config2.api 的类型是 string(精确)
// config2.timeout 的类型是 number(精确)
// 实用示例:配置对象
type Theme = {
colors: Record<string, string>
fonts: Record<string, string>
}
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
danger: '#dc3545'
},
fonts: {
body: 'system-ui',
heading: 'Georgia'
}
} satisfies Theme
// 保留了精确的字面量类型
type PrimaryColor = typeof theme.colors.primary // '#007bff'
常见问题
工具类型性能问题?
typescript
// 避免过度嵌套的工具类型
// ❌ 性能差
type DeepComplex<T> = {
[K in keyof T]: T[K] extends object
? DeepComplex<Partial<Required<Readonly<T[K]>>>>
: T[K]
}
// ✅ 拆分为多个简单类型
type Step1<T> = Readonly<T>
type Step2<T> = Required<T>
type Step3<T> = Partial<T>
条件类型不工作?
typescript
// 使用 distributive conditional types
type ToArray<T> = T extends any ? T[] : never
type Result = ToArray<string | number> // string[] | number[]
// 避免 distribution
type ToArray2<T> = [T] extends [any] ? T[] : never
type Result2 = ToArray2<string | number> // (string | number)[]
类型守卫失效?
typescript
// 确保守卫函数返回类型谓词
// ❌ 错误
function isUser(value: unknown) {
return value && typeof value === 'object' && 'id' in value
}
// ✅ 正确
function isUser(value: unknown): value is User {
return value && typeof value === 'object' && 'id' in value
}
最佳实践
DO ✅
- 充分利用 TypeScript 内置工具类型
- 封装复杂的条件类型为可复用的工具
- 综合使用各种类型守卫方式
- 等待装饰器 Stage 3 稳定后再使用
- 使用 satisfies 保留精确类型推断
DON'T ❌
- 不要重复实现内置工具类型的功能
- 避免过度嵌套的条件类型(影响性能)
- 不要在生产代码中使用实验性特性
- 避免过度使用模板字面量类型
- 不要忽略类型守卫的返回类型声明
下一步
掌握高级特性后,请参考: