TypeScript 进阶教程
本教程将深入探讨 TypeScript 的高级特性和最佳实践,帮助您成为 TypeScript 专家。
高级类型
联合类型和交叉类型
typescript
// 联合类型
type Status = 'loading' | 'success' | 'error'
type ID = string | number
// 交叉类型
interface User {
name: string
email: string
}
interface Admin {
permissions: string[]
}
type AdminUser = User & Admin
const adminUser: AdminUser = {
name: 'John',
email: 'john@example.com',
permissions: ['read', 'write', 'delete']
}
条件类型
typescript
// 基础条件类型
type IsString<T> = T extends string ? true : false
type Test1 = IsString<string> // true
type Test2 = IsString<number> // false
// 实用的条件类型
type NonNullable<T> = T extends null | undefined ? never : T
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never
type StrArrOrNumArr = ToArray<string | number> // string[] | number[]
映射类型
typescript
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 自定义映射类型
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface Person {
name: string
age: number
}
type PersonGetters = Getters<Person>
// {
// getName: () => string
// getAge: () => number
// }
模板字面量类型
typescript
// 基础模板字面量类型
type Greeting = `Hello ${string}`
type EmailLocaleIDs = `${string}_${string}`
// 结合映射类型
type EventNames<T extends string> = `${T}Changed`
type PersonEvents = EventNames<'name' | 'age'> // 'nameChanged' | 'ageChanged'
// 实用示例:CSS 属性
type CSSProperties = {
[K in
| 'color'
| 'background-color'
| 'font-size'
as `--${K}`
]?: string
}
const styles: CSSProperties = {
'--color': 'red',
'--background-color': 'blue',
'--font-size': '16px'
}
泛型进阶
泛型约束
typescript
// 基础约束
interface Lengthwise {
length: number
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}
// 使用类型参数约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const person = { name: 'John', age: 30 }
const name = getProperty(person, 'name') // string
const age = getProperty(person, 'age') // number
泛型工具类型
typescript
// 自定义工具类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
// 函数重载的泛型
function createElement<T extends keyof HTMLElementTagNameMap>(
tag: T
): HTMLElementTagNameMap[T]
function createElement<T extends HTMLElement>(
tag: string
): T
function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}
const div = createElement('div') // HTMLDivElement
const span = createElement('span') // HTMLSpanElement
const custom = createElement<HTMLElement>('custom') // HTMLElement
装饰器
类装饰器
typescript
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor)
Object.seal(constructor.prototype)
}
function classDecorator<T extends { new (...args: any[]): {} }>(
constructor: T
) {
return class extends constructor {
newProperty = 'new property'
hello = 'override'
}
}
@sealed
@classDecorator
class BugReport {
type = 'report'
title: string
constructor(t: string) {
this.title = t
}
}
方法装饰器
typescript
// 方法装饰器
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value
}
}
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args)
const result = originalMethod.apply(this, args)
console.log(`Result:`, result)
return result
}
}
class Calculator {
@enumerable(false)
@log
add(a: number, b: number): number {
return a + b
}
}
属性装饰器
typescript
// 属性装饰器
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string
const getter = () => value
const setter = (newVal: string) => {
value = formatString.replace('%s', newVal)
}
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
})
}
}
class Greeter {
@format('Hello, %s!')
greeting: string
constructor(message: string) {
this.greeting = message
}
}
const greeter = new Greeter('world')
console.log(greeter.greeting) // "Hello, world!"
模块系统
命名空间
typescript
// 命名空间
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean
}
const lettersRegexp = /^[A-Za-z]+$/
const numberRegexp = /^[0-9]+$/
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s)
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s)
}
}
}
// 使用命名空间
const validators: { [s: string]: Validation.StringValidator } = {}
validators['ZIP code'] = new Validation.ZipCodeValidator()
validators['Letters only'] = new Validation.LettersOnlyValidator()
模块声明
typescript
// 声明外部模块
declare module 'lodash' {
export function chunk<T>(array: T[], size?: number): T[][]
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait?: number,
options?: object
): T
}
// 全局声明
declare global {
interface Window {
myGlobalFunction: (name: string) => void
}
}
// 模块扩展
declare module 'vue' {
interface ComponentCustomProperties {
$myGlobalProperty: string
}
}
实用技巧
类型守卫
typescript
// 用户定义的类型守卫
function isString(value: any): value is string {
return typeof value === 'string'
}
function isNumber(value: any): value is number {
return typeof value === 'number'
}
// 使用类型守卫
function processValue(value: string | number) {
if (isString(value)) {
// value 的类型被缩小为 string
console.log(value.toUpperCase())
} else if (isNumber(value)) {
// value 的类型被缩小为 number
console.log(value.toFixed(2))
}
}
// 判别联合类型
interface Bird {
type: 'bird'
flyingSpeed: number
}
interface Horse {
type: 'horse'
runningSpeed: number
}
type Animal = Bird | Horse
function moveAnimal(animal: Animal) {
switch (animal.type) {
case 'bird':
console.log('Flying at speed: ' + animal.flyingSpeed)
break
case 'horse':
console.log('Running at speed: ' + animal.runningSpeed)
break
}
}
索引签名和映射类型
typescript
// 索引签名
interface StringDictionary {
[key: string]: string
}
interface NumberDictionary {
[key: string]: number
length: number // 可以,length 是 number 类型
name: string // 错误,name 的类型与索引类型返回值的类型不匹配
}
// 高级映射类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
interface User {
id: number
name: string
email: string
password: string
}
type PublicUser = Omit<User, 'password'> // { id: number; name: string; email: string }
type UserCredentials = Pick<User, 'email' | 'password'> // { email: string; password: string }
函数重载
typescript
// 函数重载
function reverse(x: string): string
function reverse(x: number): number
function reverse(x: boolean): boolean
function reverse(x: string | number | boolean): string | number | boolean {
if (typeof x === 'string') {
return x.split('').reverse().join('')
} else if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else {
return !x
}
}
const reversedString = reverse('hello') // string
const reversedNumber = reverse(12345) // number
const reversedBoolean = reverse(true) // boolean
实战项目:类型安全的 API 客户端
让我们创建一个类型安全的 API 客户端:
typescript
// types/api.ts
export interface User {
id: number
name: string
email: string
createdAt: string
}
export interface CreateUserRequest {
name: string
email: string
}
export interface UpdateUserRequest {
name?: string
email?: string
}
export interface ApiResponse<T> {
data: T
message: string
success: boolean
}
export interface PaginatedResponse<T> {
data: T[]
total: number
page: number
limit: number
}
// api/client.ts
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
interface RequestConfig {
method: HttpMethod
headers?: Record<string, string>
body?: any
}
class ApiClient {
private baseURL: string
private defaultHeaders: Record<string, string>
constructor(baseURL: string) {
this.baseURL = baseURL
this.defaultHeaders = {
'Content-Type': 'application/json'
}
}
private async request<T>(
endpoint: string,
config: RequestConfig
): Promise<ApiResponse<T>> {
const url = `${this.baseURL}${endpoint}`
const headers = { ...this.defaultHeaders, ...config.headers }
const response = await fetch(url, {
method: config.method,
headers,
body: config.body ? JSON.stringify(config.body) : undefined
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json()
}
async get<T>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'GET' })
}
async post<T, U = any>(
endpoint: string,
data: U
): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'POST',
body: data
})
}
async put<T, U = any>(
endpoint: string,
data: U
): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'PUT',
body: data
})
}
async delete<T>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'DELETE' })
}
}
// api/users.ts
export class UserService {
constructor(private client: ApiClient) {}
async getUsers(): Promise<ApiResponse<PaginatedResponse<User>>> {
return this.client.get<PaginatedResponse<User>>('/users')
}
async getUser(id: number): Promise<ApiResponse<User>> {
return this.client.get<User>(`/users/${id}`)
}
async createUser(data: CreateUserRequest): Promise<ApiResponse<User>> {
return this.client.post<User, CreateUserRequest>('/users', data)
}
async updateUser(
id: number,
data: UpdateUserRequest
): Promise<ApiResponse<User>> {
return this.client.put<User, UpdateUserRequest>(`/users/${id}`, data)
}
async deleteUser(id: number): Promise<ApiResponse<void>> {
return this.client.delete<void>(`/users/${id}`)
}
}
// 使用示例
const apiClient = new ApiClient('https://api.example.com')
const userService = new UserService(apiClient)
async function example() {
try {
// 获取用户列表
const usersResponse = await userService.getUsers()
console.log(usersResponse.data.data) // User[]
// 创建新用户
const newUser = await userService.createUser({
name: 'John Doe',
email: 'john@example.com'
})
console.log(newUser.data) // User
// 更新用户
const updatedUser = await userService.updateUser(1, {
name: 'Jane Doe'
})
console.log(updatedUser.data) // User
} catch (error) {
console.error('API Error:', error)
}
}
性能优化
类型推断优化
typescript
// 避免过度注解
// ❌ 不好
const users: User[] = await getUsers()
// ✅ 好
const users = await getUsers() // TypeScript 可以推断类型
// 使用 const 断言
// ❌ 不好
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
} // 类型为 { apiUrl: string; timeout: number }
// ✅ 好
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
} as const // 类型为 { readonly apiUrl: "https://api.example.com"; readonly timeout: 5000 }
编译优化
json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"skipLibCheck": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
测试
类型测试
typescript
// 类型测试工具
type Expect<T extends true> = T
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
? true
: false
// 测试用例
type cases = [
Expect<Equal<Pick<User, 'name'>, { name: string }>>,
Expect<Equal<Omit<User, 'id'>, { name: string; email: string; createdAt: string }>>,
]
// 运行时测试
import { describe, it, expect } from 'vitest'
describe('UserService', () => {
it('should create user with correct type', async () => {
const userService = new UserService(apiClient)
const userData: CreateUserRequest = {
name: 'Test User',
email: 'test@example.com'
}
const response = await userService.createUser(userData)
expect(response.data).toHaveProperty('id')
expect(response.data).toHaveProperty('name', userData.name)
expect(response.data).toHaveProperty('email', userData.email)
})
})
最佳实践
1. 类型设计原则
typescript
// ✅ 使用联合类型而不是枚举
type Theme = 'light' | 'dark' | 'auto'
// ✅ 使用接口扩展
interface BaseEntity {
id: string
createdAt: Date
updatedAt: Date
}
interface User extends BaseEntity {
name: string
email: string
}
// ✅ 使用泛型约束
function processItems<T extends { id: string }>(items: T[]): T[] {
return items.filter(item => item.id.length > 0)
}
2. 错误处理
typescript
// 结果类型模式
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E }
async function safeApiCall<T>(
apiCall: () => Promise<T>
): Promise<Result<T>> {
try {
const data = await apiCall()
return { success: true, data }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error(String(error))
}
}
}
// 使用
const result = await safeApiCall(() => userService.getUser(1))
if (result.success) {
console.log(result.data) // User
} else {
console.error(result.error) // Error
}
3. 配置管理
typescript
// 环境配置
interface Config {
readonly apiUrl: string
readonly timeout: number
readonly retries: number
readonly debug: boolean
}
const createConfig = (env: string): Config => {
const baseConfig = {
timeout: 5000,
retries: 3,
debug: false
}
switch (env) {
case 'development':
return {
...baseConfig,
apiUrl: 'http://localhost:3000',
debug: true
}
case 'production':
return {
...baseConfig,
apiUrl: 'https://api.production.com'
}
default:
throw new Error(`Unknown environment: ${env}`)
}
}
export const config = createConfig(process.env.NODE_ENV || 'development')
总结
TypeScript 进阶特性让我们能够:
- 类型安全 - 在编译时捕获错误
- 代码提示 - 更好的开发体验
- 重构支持 - 安全地重构代码
- 文档化 - 类型即文档
- 团队协作 - 统一的代码规范
下一步学习
参考资源
掌握这些 TypeScript 进阶特性,您就能写出更安全、更优雅的代码!🚀