TypeScript现代开发实践指南
TypeScript是JavaScript的超集,通过添加静态类型检查,为大型应用开发提供了更好的可维护性、可读性和开发体验。随着前端应用复杂度的增加,TypeScript已成为现代前端开发的标准选择。
核心价值
TypeScript = 类型安全 + 开发体验 + 工程化 + 生态支持
- 🛡️ 类型安全:编译时错误检查,减少运行时错误
- 🔧 开发体验:智能提示、重构支持、导航跳转
- 📦 工程化支持:模块系统、装饰器、编译优化
- 🌐 生态丰富:与主流框架和工具深度集成
- 📚 渐进式采用:可以逐步从JavaScript迁移
- 🎯 团队协作:统一的类型约定,提高代码质量
1. TypeScript类型系统深度解析
1.1 类型系统架构
TypeScript的类型系统是其核心特性,提供了从基础类型到高级类型的完整解决方案。
类型系统特性对比
| 类型特性 | JavaScript | TypeScript | 优势 | 适用场景 |
|---|---|---|---|---|
| 静态类型检查 | ❌ | ✅ | 编译时发现错误 | 大型项目 |
| 类型推断 | ❌ | ✅ | 减少类型注解 | 提升开发效率 |
| 接口定义 | ❌ | ✅ | 契约式编程 | API设计 |
| 泛型支持 | ❌ | ✅ | 类型复用 | 工具函数 |
| 装饰器 | 实验性 | ✅ | 元编程 | 框架开发 |
| 枚举类型 | ❌ | ✅ | 常量管理 | 状态定义 |
- 基础类型系统
- 接口与类型别名
- 泛型系统
基础类型与类型注解
TypeScript基础类型完整示例
typescript
1// 1. 原始类型2const userName: string = 'John Doe'3const userAge: number = 304const isActive: boolean = true5const userSymbol: symbol = Symbol('user')6const userBigInt: bigint = 100n78// 2. 字面量类型9type Theme = 'light' | 'dark' | 'auto'10type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'11type StatusCode = 200 | 404 | 5001213const currentTheme: Theme = 'dark'14const apiMethod: HttpMethod = 'POST'1516// 3. 数组类型17const numbers: number[] = [1, 2, 3, 4, 5]18const strings: Array<string> = ['hello', 'world']19const mixed: (string | number)[] = ['hello', 42, 'world']2021// 只读数组22const readonlyNumbers: readonly number[] = [1, 2, 3]23const readonlyStrings: ReadonlyArray<string> = ['a', 'b', 'c']2425// 4. 元组类型26type Coordinate = [number, number]27type NamedCoordinate = [x: number, y: number]28type OptionalTuple = [string, number?]29type RestTuple = [string, ...number[]]3031const point: Coordinate = [10, 20]32const namedPoint: NamedCoordinate = [10, 20]33const optionalPoint: OptionalTuple = ['origin']34const restPoint: RestTuple = ['point', 1, 2, 3]3536// 5. 对象类型37interface User {38 readonly id: number39 name: string40 email?: string41 age: number42 preferences: {43 theme: Theme44 notifications: boolean45 }46}4748type UserUpdate = {49 name?: string50 email?: string51 age?: number52}5354// 索引签名55interface Dictionary<T> {56 [key: string]: T57}5859interface NumberDictionary {60 [index: string]: number61 length: number // 必须是number类型62}6364// 6. 函数类型65type AddFunction = (a: number, b: number) => number66type AsyncFunction<T> = (data: T) => Promise<void>67type EventHandler<T = Event> = (event: T) => void6869const add: AddFunction = (a, b) => a + b70const processData: AsyncFunction<User> = async (user) => {71 console.log(`Processing user: ${user.name}`)72}7374// 函数重载75function createUser(name: string): User76function createUser(id: number, name: string): User77function createUser(idOrName: string | number, name?: string): User {78 if (typeof idOrName === 'string') {79 return {80 id: Math.random(),81 name: idOrName,82 age: 0,83 preferences: { theme: 'light', notifications: true }84 }85 } else {86 return {87 id: idOrName,88 name: name!,89 age: 0,90 preferences: { theme: 'light', notifications: true }91 }92 }93}9495// 7. 联合类型和交叉类型96type StringOrNumber = string | number97type UserWithTimestamp = User & {98 createdAt: Date99 updatedAt: Date100}101102// 判别联合类型103interface LoadingState {104 status: 'loading'105}106107interface SuccessState {108 status: 'success'109 data: any110}111112interface ErrorState {113 status: 'error'114 error: string115}116117type AsyncState = LoadingState | SuccessState | ErrorState118119function handleState(state: AsyncState) {120 switch (state.status) {121 case 'loading':122 console.log('Loading...')123 break124 case 'success':125 console.log('Data:', state.data) // TypeScript知道这里有data属性126 break127 case 'error':128 console.log('Error:', state.error) // TypeScript知道这里有error属性129 break130 }131}132133// 8. 类型守卫134function isString(value: unknown): value is string {135 return typeof value === 'string'136}137138function isUser(obj: any): obj is User {139 return obj && 140 typeof obj.id === 'number' &&141 typeof obj.name === 'string' &&142 typeof obj.age === 'number'143}144145// 使用类型守卫146function processValue(value: unknown) {147 if (isString(value)) {148 // TypeScript知道value是string类型149 console.log(value.toUpperCase())150 }151 152 if (isUser(value)) {153 // TypeScript知道value是User类型154 console.log(`User: ${value.name}, Age: ${value.age}`)155 }156}157158// 9. 类型断言159const userInput = document.getElementById('user-input') as HTMLInputElement160const apiResponse = JSON.parse('{"name": "John"}') as User161162// 非空断言163function processUser(user: User | null) {164 // 确定user不为null时使用165 console.log(user!.name)166}167168// 10. 类型缩窄169function processStringOrNumber(value: string | number) {170 if (typeof value === 'string') {171 // 在这个分支中,TypeScript知道value是string172 return value.toUpperCase()173 } else {174 // 在这个分支中,TypeScript知道value是number175 return value.toFixed(2)176 }177}178179// 11. never类型180function throwError(message: string): never {181 throw new Error(message)182}183184function exhaustiveCheck(value: never): never {185 throw new Error(`Unexpected value: ${value}`)186}187188// 在switch语句中使用never确保完整性189function handleTheme(theme: Theme) {190 switch (theme) {191 case 'light':192 return 'Light theme'193 case 'dark':194 return 'Dark theme'195 case 'auto':196 return 'Auto theme'197 default:198 // 如果添加新的Theme值但忘记处理,TypeScript会报错199 return exhaustiveCheck(theme)200 }201}接口设计与类型别名
接口与类型别名最佳实践
typescript
1// 1. 基础接口定义2interface BaseEntity {3 readonly id: string4 createdAt: Date5 updatedAt: Date6}78interface User extends BaseEntity {9 name: string10 email: string11 age?: number12 avatar?: string13 status: 'active' | 'inactive' | 'pending'14}1516// 2. 可选属性和只读属性17interface UserPreferences {18 readonly userId: string19 theme?: 'light' | 'dark' | 'auto'20 language?: string21 notifications?: {22 email: boolean23 push: boolean24 sms: boolean25 }26}2728// 3. 索引签名29interface ApiResponse<T = any> {30 success: boolean31 data?: T32 error?: string33 meta?: {34 [key: string]: any35 }36}3738// 4. 函数接口39interface EventEmitter {40 on(event: string, listener: (...args: any[]) => void): void41 off(event: string, listener: (...args: any[]) => void): void42 emit(event: string, ...args: any[]): void43}4445interface HttpClient {46 get<T>(url: string, config?: RequestConfig): Promise<ApiResponse<T>>47 post<T>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>>48 put<T>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>>49 delete<T>(url: string, config?: RequestConfig): Promise<ApiResponse<T>>50}5152// 5. 接口继承和组合53interface Timestamped {54 createdAt: Date55 updatedAt: Date56}5758interface Auditable {59 createdBy: string60 updatedBy: string61}6263interface Post extends BaseEntity, Auditable {64 title: string65 content: string66 author: User67 tags: string[]68 published: boolean69}7071// 6. 类型别名 vs 接口72// 类型别名 - 适用于联合类型、原始类型、计算类型73type Status = 'loading' | 'success' | 'error'74type EventHandler<T> = (event: T) => void75type DeepReadonly<T> = {76 readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]77}7879// 接口 - 适用于对象形状定义、类实现、声明合并80interface UserService {81 getUser(id: string): Promise<User>82 updateUser(id: string, updates: Partial<User>): Promise<User>83 deleteUser(id: string): Promise<void>84}8586// 7. 声明合并87interface Window {88 customProperty: string89}9091interface Window {92 anotherProperty: number93}9495// 现在Window接口包含两个属性9697// 8. 条件类型和映射类型98type NonNullable<T> = T extends null | undefined ? never : T99type Partial<T> = {100 [P in keyof T]?: T[P]101}102103type Required<T> = {104 [P in keyof T]-?: T[P]105}106107type Pick<T, K extends keyof T> = {108 [P in K]: T[P]109}110111type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>112113// 9. 实用工具类型应用114type UserCreateInput = Omit<User, 'id' | 'createdAt' | 'updatedAt'>115type UserUpdateInput = Partial<Pick<User, 'name' | 'email' | 'age' | 'avatar'>>116type UserPublicInfo = Pick<User, 'id' | 'name' | 'avatar'>117118// 10. 高级类型模式119// 递归类型120type DeepPartial<T> = {121 [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]122}123124// 模板字面量类型125type EventName<T extends string> = `on${Capitalize<T>}`126type ApiEndpoint<T extends string> = `/api/${T}`127128type UserEvents = EventName<'userCreated' | 'userUpdated' | 'userDeleted'>129// 结果: 'onUserCreated' | 'onUserUpdated' | 'onUserDeleted'130131// 11. 品牌类型(Branded Types)132type UserId = string & { readonly brand: unique symbol }133type Email = string & { readonly brand: unique symbol }134135function createUserId(id: string): UserId {136 return id as UserId137}138139function createEmail(email: string): Email {140 if (!email.includes('@')) {141 throw new Error('Invalid email')142 }143 return email as Email144}145146// 12. 类型安全的事件系统147interface EventMap {148 'user:created': { user: User }149 'user:updated': { user: User; changes: Partial<User> }150 'user:deleted': { userId: string }151 'system:error': { error: Error; context?: string }152}153154class TypedEventEmitter {155 private listeners: {156 [K in keyof EventMap]?: Array<(data: EventMap[K]) => void>157 } = {}158159 on<K extends keyof EventMap>(160 event: K,161 listener: (data: EventMap[K]) => void162 ): void {163 if (!this.listeners[event]) {164 this.listeners[event] = []165 }166 this.listeners[event]!.push(listener)167 }168169 emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {170 const eventListeners = this.listeners[event]171 if (eventListeners) {172 eventListeners.forEach(listener => listener(data))173 }174 }175176 off<K extends keyof EventMap>(177 event: K,178 listener: (data: EventMap[K]) => void179 ): void {180 const eventListeners = this.listeners[event]181 if (eventListeners) {182 const index = eventListeners.indexOf(listener)183 if (index > -1) {184 eventListeners.splice(index, 1)185 }186 }187 }188}189190// 使用示例191const eventEmitter = new TypedEventEmitter()192193eventEmitter.on('user:created', (data) => {194 // data的类型自动推断为 { user: User }195 console.log(`User created: ${data.user.name}`)196})197198eventEmitter.emit('user:created', {199 user: {200 id: '1',201 name: 'John',202 email: 'john@example.com',203 status: 'active',204 createdAt: new Date(),205 updatedAt: new Date()206 }207})208209// 13. API客户端类型安全210interface ApiEndpoints {211 'GET /users': {212 response: User[]213 }214 'GET /users/:id': {215 params: { id: string }216 response: User217 }218 'POST /users': {219 body: UserCreateInput220 response: User221 }222 'PUT /users/:id': {223 params: { id: string }224 body: UserUpdateInput225 response: User226 }227 'DELETE /users/:id': {228 params: { id: string }229 response: void230 }231}232233type ExtractParams<T> = T extends { params: infer P } ? P : never234type ExtractBody<T> = T extends { body: infer B } ? B : never235type ExtractResponse<T> = T extends { response: infer R } ? R : never236237class TypedApiClient {238 async request<K extends keyof ApiEndpoints>(239 endpoint: K,240 options?: {241 params?: ExtractParams<ApiEndpoints[K]>242 body?: ExtractBody<ApiEndpoints[K]>243 }244 ): Promise<ExtractResponse<ApiEndpoints[K]>> {245 // 实际的HTTP请求实现246 throw new Error('Not implemented')247 }248}249250// 使用示例251const apiClient = new TypedApiClient()252253// TypeScript会自动推断参数和返回类型254const user = await apiClient.request('GET /users/:id', {255 params: { id: '123' }256}) // user的类型是User257258const newUser = await apiClient.request('POST /users', {259 body: {260 name: 'John',261 email: 'john@example.com',262 status: 'active'263 }264}) // newUser的类型是User泛型编程与类型约束
泛型系统深度应用
typescript
1// 1. 基础泛型2function identity<T>(arg: T): T {3 return arg4}56// 泛型接口7interface GenericResponse<T> {8 data: T9 success: boolean10 message?: string11}1213// 泛型类14class GenericRepository<T, K = string> {15 private items: Map<K, T> = new Map()1617 add(key: K, item: T): void {18 this.items.set(key, item)19 }2021 get(key: K): T | undefined {22 return this.items.get(key)23 }2425 getAll(): T[] {26 return Array.from(this.items.values())27 }2829 delete(key: K): boolean {30 return this.items.delete(key)31 }32}3334// 2. 泛型约束35interface Lengthwise {36 length: number37}3839function loggingIdentity<T extends Lengthwise>(arg: T): T {40 console.log(arg.length) // 现在我们知道arg有length属性41 return arg42}4344// 使用类型参数约束45function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {46 return obj[key]47}4849const person = { name: 'John', age: 30, email: 'john@example.com' }50const name = getProperty(person, 'name') // string类型51const age = getProperty(person, 'age') // number类型5253// 3. 条件类型54type NonNullable<T> = T extends null | undefined ? never : T55type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any56type Parameters<T> = T extends (...args: infer P) => any ? P : never5758// 自定义条件类型59type IsArray<T> = T extends any[] ? true : false60type ArrayElement<T> = T extends (infer U)[] ? U : never6162type Test1 = IsArray<string[]> // true63type Test2 = IsArray<string> // false64type Test3 = ArrayElement<User[]> // User6566// 4. 映射类型67type Readonly<T> = {68 readonly [P in keyof T]: T[P]69}7071type Partial<T> = {72 [P in keyof T]?: T[P]73}7475type Nullable<T> = {76 [P in keyof T]: T[P] | null77}7879// 高级映射类型80type DeepReadonly<T> = {81 readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]82}8384type OptionalKeys<T> = {85 [K in keyof T]-?: {} extends Pick<T, K> ? K : never86}[keyof T]8788type RequiredKeys<T> = {89 [K in keyof T]-?: {} extends Pick<T, K> ? never : K90}[keyof T]9192// 5. 实用泛型工具93// 深度合并类型94type DeepMerge<T, U> = {95 [K in keyof T | keyof U]: K extends keyof U96 ? K extends keyof T97 ? T[K] extends object98 ? U[K] extends object99 ? DeepMerge<T[K], U[K]>100 : U[K]101 : U[K]102 : U[K]103 : K extends keyof T104 ? T[K]105 : never106}107108// 类型安全的对象更新109function updateObject<T, U extends Partial<T>>(110 original: T,111 updates: U112): T & U {113 return { ...original, ...updates }114}115116// 6. 高阶函数泛型117// 函数组合118function compose<A, B, C>(119 f: (b: B) => C,120 g: (a: A) => B121): (a: A) => C {122 return (a: A) => f(g(a))123}124125// 管道操作126function pipe<T>(...fns: Array<(arg: T) => T>): (arg: T) => T {127 return (arg: T) => fns.reduce((acc, fn) => fn(acc), arg)128}129130// 柯里化131function curry<A, B, C>(132 fn: (a: A, b: B) => C133): (a: A) => (b: B) => C {134 return (a: A) => (b: B) => fn(a, b)135}136137// 7. 异步泛型模式138// Promise工具类型139type PromiseValue<T> = T extends Promise<infer U> ? U : T140type AwaitedReturnType<T> = T extends (...args: any[]) => Promise<infer R> ? R : never141142// 异步数据获取器143class AsyncDataFetcher<T> {144 private cache = new Map<string, T>()145146 async fetch<K extends keyof T>(147 key: K,148 fetcher: () => Promise<T[K]>149 ): Promise<T[K]> {150 const cacheKey = String(key)151 152 if (this.cache.has(cacheKey)) {153 return this.cache.get(cacheKey) as T[K]154 }155156 const data = await fetcher()157 this.cache.set(cacheKey, data as any)158 return data159 }160161 invalidate<K extends keyof T>(key: K): void {162 this.cache.delete(String(key))163 }164165 clear(): void {166 this.cache.clear()167 }168}169170// 8. 状态管理泛型171interface State {172 user: User | null173 posts: Post[]174 loading: boolean175 error: string | null176}177178type Action<T extends keyof State> = {179 type: T180 payload: State[T]181}182183type ActionCreator<T extends keyof State> = (payload: State[T]) => Action<T>184185function createActionCreator<T extends keyof State>(186 type: T187): ActionCreator<T> {188 return (payload: State[T]) => ({ type, payload })189}190191// 使用示例192const setUser = createActionCreator('user')193const setPosts = createActionCreator('posts')194const setLoading = createActionCreator('loading')195196// 9. 表单验证泛型197type ValidationRule<T> = (value: T) => string | null198type ValidationRules<T> = {199 [K in keyof T]?: ValidationRule<T[K]>[]200}201202type ValidationErrors<T> = {203 [K in keyof T]?: string204}205206class FormValidator<T extends Record<string, any>> {207 constructor(private rules: ValidationRules<T>) {}208209 validate(data: T): ValidationErrors<T> {210 const errors: ValidationErrors<T> = {}211212 for (const key in this.rules) {213 const fieldRules = this.rules[key]214 if (fieldRules) {215 for (const rule of fieldRules) {216 const error = rule(data[key])217 if (error) {218 errors[key] = error219 break // 只显示第一个错误220 }221 }222 }223 }224225 return errors226 }227228 isValid(data: T): boolean {229 const errors = this.validate(data)230 return Object.keys(errors).length === 0231 }232}233234// 使用示例235interface UserForm {236 name: string237 email: string238 age: number239}240241const userValidator = new FormValidator<UserForm>({242 name: [243 (value) => value.length === 0 ? '姓名不能为空' : null,244 (value) => value.length < 2 ? '姓名至少2个字符' : null245 ],246 email: [247 (value) => value.length === 0 ? '邮箱不能为空' : null,248 (value) => !value.includes('@') ? '邮箱格式不正确' : null249 ],250 age: [251 (value) => value < 0 ? '年龄不能为负数' : null,252 (value) => value > 150 ? '年龄不能超过150' : null253 ]254})255256// 10. 类型安全的事件总线257type EventBusEvents = {258 'user:login': { user: User }259 'user:logout': { userId: string }260 'post:created': { post: Post }261 'post:updated': { post: Post; changes: Partial<Post> }262}263264class TypeSafeEventBus {265 private listeners: {266 [K in keyof EventBusEvents]?: Set<(data: EventBusEvents[K]) => void>267 } = {}268269 on<K extends keyof EventBusEvents>(270 event: K,271 listener: (data: EventBusEvents[K]) => void272 ): () => void {273 if (!this.listeners[event]) {274 this.listeners[event] = new Set()275 }276 277 this.listeners[event]!.add(listener)278 279 // 返回取消订阅函数280 return () => {281 this.listeners[event]?.delete(listener)282 }283 }284285 emit<K extends keyof EventBusEvents>(286 event: K,287 data: EventBusEvents[K]288 ): void {289 const eventListeners = this.listeners[event]290 if (eventListeners) {291 eventListeners.forEach(listener => listener(data))292 }293 }294295 once<K extends keyof EventBusEvents>(296 event: K,297 listener: (data: EventBusEvents[K]) => void298 ): void {299 const onceListener = (data: EventBusEvents[K]) => {300 listener(data)301 this.listeners[event]?.delete(onceListener)302 }303 304 this.on(event, onceListener)305 }306}307308// 使用示例309const eventBus = new TypeSafeEventBus()310311const unsubscribe = eventBus.on('user:login', (data) => {312 console.log(`User ${data.user.name} logged in`)313})314315eventBus.emit('user:login', {316 user: {317 id: '1',318 name: 'John',319 email: 'john@example.com',320 status: 'active',321 createdAt: new Date(),322 updatedAt: new Date()323 }324})325326// 取消订阅327unsubscribe()2. TypeScript工程化配置
2.1 编译配置与优化
TypeScript的编译配置是项目工程化的重要组成部分,合理的配置可以提升开发体验和构建性能。
编译配置对比
| 配置项 | 开发环境 | 生产环境 | 说明 |
|---|---|---|---|
| target | ES2020 | ES2018 | 编译目标版本 |
| module | ESNext | CommonJS | 模块系统 |
| sourceMap | true | false | 源码映射 |
| strict | true | true | 严格模式 |
| incremental | true | false | 增量编译 |
| skipLibCheck | true | true | 跳过库检查 |
- 配置文件
- 项目结构
- 构建工具集成
完整的tsconfig.json配置
tsconfig.json完整配置
json
1{2 "compilerOptions": {3 // 基础配置4 "target": "ES2020",5 "module": "ESNext",6 "lib": ["ES2020", "DOM", "DOM.Iterable"],7 "moduleResolution": "node",8 "allowJs": true,9 "checkJs": false,10 "jsx": "react-jsx",11 12 // 输出配置13 "outDir": "./dist",14 "rootDir": "./src",15 "removeComments": true,16 "noEmit": false,17 "importHelpers": true,18 "downlevelIteration": true,19 "isolatedModules": true,20 21 // 源码映射22 "sourceMap": true,23 "inlineSourceMap": false,24 "declarationMap": true,25 26 // 声明文件27 "declaration": true,28 "declarationDir": "./types",29 "emitDeclarationOnly": false,30 31 // 严格检查32 "strict": true,33 "noImplicitAny": true,34 "strictNullChecks": true,35 "strictFunctionTypes": true,36 "strictBindCallApply": true,37 "strictPropertyInitialization": true,38 "noImplicitThis": true,39 "noImplicitReturns": true,40 "noFallthroughCasesInSwitch": true,41 "noUncheckedIndexedAccess": true,42 43 // 额外检查44 "noUnusedLocals": true,45 "noUnusedParameters": true,46 "exactOptionalPropertyTypes": true,47 "noImplicitOverride": true,48 "noPropertyAccessFromIndexSignature": true,49 50 // 模块解析51 "baseUrl": "./",52 "paths": {53 "@/*": ["src/*"],54 "@/components/*": ["src/components/*"],55 "@/utils/*": ["src/utils/*"],56 "@/types/*": ["src/types/*"],57 "@/hooks/*": ["src/hooks/*"],58 "@/stores/*": ["src/stores/*"],59 "@/services/*": ["src/services/*"]60 },61 "typeRoots": ["./node_modules/@types", "./src/types"],62 "types": ["node", "jest", "@testing-library/jest-dom"],63 64 // 性能优化65 "incremental": true,66 "tsBuildInfoFile": "./node_modules/.cache/typescript/tsbuildinfo",67 "skipLibCheck": true,68 "skipDefaultLibCheck": true,69 70 // 互操作性71 "esModuleInterop": true,72 "allowSyntheticDefaultImports": true,73 "forceConsistentCasingInFileNames": true,74 "resolveJsonModule": true,75 76 // 实验性功能77 "experimentalDecorators": true,78 "emitDecoratorMetadata": true,79 "useDefineForClassFields": true80 },81 82 // 包含和排除83 "include": [84 "src/**/*",85 "tests/**/*",86 "*.d.ts"87 ],88 "exclude": [89 "node_modules",90 "dist",91 "build",92 "coverage",93 "**/*.spec.ts",94 "**/*.test.ts"95 ],96 97 // 项目引用98 "references": [99 { "path": "./packages/shared" },100 { "path": "./packages/utils" }101 ],102 103 // 监听配置104 "watchOptions": {105 "watchFile": "useFsEvents",106 "watchDirectory": "useFsEvents",107 "fallbackPolling": "dynamicPriority",108 "synchronousWatchDirectory": true,109 "excludeDirectories": ["**/node_modules", "dist"]110 },111 112 // TypeScript 4.9+ 配置113 "compilerOptions": {114 "moduleDetection": "auto",115 "allowImportingTsExtensions": false,116 "allowArbitraryExtensions": false117 }118}环境特定配置
tsconfig.dev.json - 开发环境配置
json
1{2 "extends": "./tsconfig.json",3 "compilerOptions": {4 "target": "ES2022",5 "module": "ESNext",6 "sourceMap": true,7 "incremental": true,8 "noEmit": true,9 "preserveWatchOutput": true,10 11 // 开发时放宽的检查12 "noUnusedLocals": false,13 "noUnusedParameters": false,14 "skipLibCheck": true15 },16 "include": [17 "src/**/*",18 "tests/**/*",19 "vite.config.ts",20 "vitest.config.ts"21 ]22}tsconfig.build.json - 构建配置
json
1{2 "extends": "./tsconfig.json",3 "compilerOptions": {4 "target": "ES2018",5 "module": "CommonJS",6 "sourceMap": false,7 "incremental": false,8 "removeComments": true,9 "declaration": true,10 "declarationMap": false,11 12 // 生产构建优化13 "noEmitOnError": true,14 "listEmittedFiles": true,15 "listFiles": false16 },17 "exclude": [18 "src/**/*.test.ts",19 "src/**/*.spec.ts",20 "src/**/*.stories.ts",21 "tests/**/*"22 ]23}TypeScript项目结构最佳实践
项目结构与类型组织
typescript
1// src/types/index.ts - 全局类型定义2export interface BaseEntity {3 id: string4 createdAt: Date5 updatedAt: Date6}78export interface User extends BaseEntity {9 name: string10 email: string11 role: UserRole12 status: UserStatus13}1415export type UserRole = 'admin' | 'user' | 'guest'16export type UserStatus = 'active' | 'inactive' | 'pending'1718// API相关类型19export interface ApiResponse<T = any> {20 success: boolean21 data?: T22 error?: ApiError23 meta?: ApiMeta24}2526export interface ApiError {27 code: string28 message: string29 details?: Record<string, any>30}3132export interface ApiMeta {33 page?: number34 limit?: number35 total?: number36 hasMore?: boolean37}3839// src/types/api.ts - API类型定义40import { User, ApiResponse } from './index'4142export namespace UserAPI {43 export interface GetUsersRequest {44 page?: number45 limit?: number46 search?: string47 role?: UserRole48 status?: UserStatus49 }5051 export interface GetUsersResponse extends ApiResponse<User[]> {52 meta: {53 page: number54 limit: number55 total: number56 hasMore: boolean57 }58 }5960 export interface CreateUserRequest {61 name: string62 email: string63 role: UserRole64 }6566 export interface UpdateUserRequest {67 name?: string68 email?: string69 role?: UserRole70 status?: UserStatus71 }72}7374// src/types/components.ts - 组件类型定义75import { ReactNode, HTMLAttributes } from 'react'7677export interface BaseComponentProps {78 className?: string79 children?: ReactNode80 testId?: string81}8283export interface ButtonProps extends BaseComponentProps {84 variant?: 'primary' | 'secondary' | 'danger'85 size?: 'small' | 'medium' | 'large'86 disabled?: boolean87 loading?: boolean88 onClick?: () => void89}9091export interface ModalProps extends BaseComponentProps {92 visible: boolean93 title?: string94 width?: number | string95 closable?: boolean96 onClose?: () => void97 onCancel?: () => void98 onOk?: () => void99}100101// src/utils/types.ts - 工具类型102export type DeepPartial<T> = {103 [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]104}105106export type DeepRequired<T> = {107 [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P]108}109110export type KeysOfType<T, U> = {111 [K in keyof T]: T[K] extends U ? K : never112}[keyof T]113114export type NonEmptyArray<T> = [T, ...T[]]115116export type Prettify<T> = {117 [K in keyof T]: T[K]118} & {}119120// 函数工具类型121export type AsyncReturnType<T extends (...args: any) => Promise<any>> = 122 T extends (...args: any) => Promise<infer R> ? R : any123124export type PromiseType<T> = T extends Promise<infer U> ? U : T125126// src/services/types.ts - 服务类型定义127export interface HttpClientConfig {128 baseURL: string129 timeout: number130 headers?: Record<string, string>131 interceptors?: {132 request?: (config: any) => any133 response?: (response: any) => any134 error?: (error: any) => any135 }136}137138export interface CacheConfig {139 ttl: number140 maxSize: number141 strategy: 'lru' | 'fifo' | 'lfu'142}143144export interface ServiceConfig {145 http: HttpClientConfig146 cache: CacheConfig147 retry: {148 attempts: number149 delay: number150 backoff: 'linear' | 'exponential'151 }152}153154// src/hooks/types.ts - Hook类型定义155export interface UseApiOptions<T> {156 initialData?: T157 enabled?: boolean158 refetchOnWindowFocus?: boolean159 refetchInterval?: number160 onSuccess?: (data: T) => void161 onError?: (error: Error) => void162}163164export interface UseApiResult<T> {165 data: T | undefined166 loading: boolean167 error: Error | null168 refetch: () => Promise<void>169 mutate: (data: T) => void170}171172export interface UsePaginationOptions {173 initialPage?: number174 initialPageSize?: number175 total?: number176}177178export interface UsePaginationResult {179 page: number180 pageSize: number181 total: number182 totalPages: number183 hasNext: boolean184 hasPrev: boolean185 goToPage: (page: number) => void186 nextPage: () => void187 prevPage: () => void188 setPageSize: (size: number) => void189}190191// src/stores/types.ts - 状态管理类型192export interface RootState {193 user: UserState194 app: AppState195 ui: UIState196}197198export interface UserState {199 currentUser: User | null200 loading: boolean201 error: string | null202}203204export interface AppState {205 theme: 'light' | 'dark' | 'auto'206 language: string207 sidebarCollapsed: boolean208}209210export interface UIState {211 notifications: Notification[]212 modals: Modal[]213 loading: Record<string, boolean>214}215216// 动作类型217export type UserAction = 218 | { type: 'USER_LOGIN_START' }219 | { type: 'USER_LOGIN_SUCCESS'; payload: User }220 | { type: 'USER_LOGIN_FAILURE'; payload: string }221 | { type: 'USER_LOGOUT' }222 | { type: 'USER_UPDATE'; payload: Partial<User> }223224// src/constants/types.ts - 常量类型225export const HTTP_STATUS = {226 OK: 200,227 CREATED: 201,228 BAD_REQUEST: 400,229 UNAUTHORIZED: 401,230 FORBIDDEN: 403,231 NOT_FOUND: 404,232 INTERNAL_SERVER_ERROR: 500233} as const234235export type HttpStatus = typeof HTTP_STATUS[keyof typeof HTTP_STATUS]236237export const USER_ROLES = {238 ADMIN: 'admin',239 USER: 'user',240 GUEST: 'guest'241} as const242243export type UserRole = typeof USER_ROLES[keyof typeof USER_ROLES]244245// 环境变量类型246declare global {247 namespace NodeJS {248 interface ProcessEnv {249 NODE_ENV: 'development' | 'production' | 'test'250 REACT_APP_API_URL: string251 REACT_APP_APP_NAME: string252 REACT_APP_VERSION: string253 }254 }255}256257// 扩展第三方库类型258declare module 'react' {259 interface HTMLAttributes<T> {260 'data-testid'?: string261 }262}263264declare module '*.svg' {265 const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>266 export default content267}268269declare module '*.png' {270 const content: string271 export default content272}273274declare module '*.jpg' {275 const content: string276 export default content277}构建工具与TypeScript集成
Vite + TypeScript配置
typescript
1// vite.config.ts2import { defineConfig } from 'vite'3import react from '@vitejs/plugin-react'4import { resolve } from 'path'5import dts from 'vite-plugin-dts'6import { visualizer } from 'rollup-plugin-visualizer'78export default defineConfig({9 plugins: [10 react(),11 12 // 生成类型声明文件13 dts({14 insertTypesEntry: true,15 include: ['src/**/*'],16 exclude: ['src/**/*.test.ts', 'src/**/*.spec.ts']17 }),18 19 // 构建分析20 visualizer({21 filename: 'dist/stats.html',22 open: true,23 gzipSize: true24 })25 ],26 27 // 路径别名28 resolve: {29 alias: {30 '@': resolve(__dirname, 'src'),31 '@/components': resolve(__dirname, 'src/components'),32 '@/utils': resolve(__dirname, 'src/utils'),33 '@/types': resolve(__dirname, 'src/types'),34 '@/hooks': resolve(__dirname, 'src/hooks'),35 '@/stores': resolve(__dirname, 'src/stores'),36 '@/services': resolve(__dirname, 'src/services')37 }38 },39 40 // 构建配置41 build: {42 target: 'es2018',43 lib: {44 entry: resolve(__dirname, 'src/index.ts'),45 name: 'MyLibrary',46 formats: ['es', 'cjs', 'umd']47 },48 rollupOptions: {49 external: ['react', 'react-dom'],50 output: {51 globals: {52 react: 'React',53 'react-dom': 'ReactDOM'54 }55 }56 },57 sourcemap: true,58 minify: 'terser',59 terserOptions: {60 compress: {61 drop_console: true,62 drop_debugger: true63 }64 }65 },66 67 // 开发服务器68 server: {69 port: 3000,70 open: true,71 cors: true72 },73 74 // 环境变量75 define: {76 __APP_VERSION__: JSON.stringify(process.env.npm_package_version)77 }78})7980// webpack.config.ts (如果使用Webpack)81import path from 'path'82import { Configuration } from 'webpack'83import HtmlWebpackPlugin from 'html-webpack-plugin'84import MiniCssExtractPlugin from 'mini-css-extract-plugin'85import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'8687const config: Configuration = {88 entry: './src/index.tsx',89 90 output: {91 path: path.resolve(__dirname, 'dist'),92 filename: '[name].[contenthash].js',93 clean: true94 },95 96 resolve: {97 extensions: ['.ts', '.tsx', '.js', '.jsx'],98 alias: {99 '@': path.resolve(__dirname, 'src'),100 '@/components': path.resolve(__dirname, 'src/components'),101 '@/utils': path.resolve(__dirname, 'src/utils'),102 '@/types': path.resolve(__dirname, 'src/types')103 }104 },105 106 module: {107 rules: [108 {109 test: /\.tsx?$/,110 use: [111 {112 loader: 'ts-loader',113 options: {114 transpileOnly: true, // 类型检查交给ForkTsCheckerWebpackPlugin115 configFile: 'tsconfig.build.json'116 }117 }118 ],119 exclude: /node_modules/120 },121 {122 test: /\.css$/,123 use: [124 MiniCssExtractPlugin.loader,125 'css-loader',126 'postcss-loader'127 ]128 }129 ]130 },131 132 plugins: [133 new HtmlWebpackPlugin({134 template: './public/index.html'135 }),136 137 new MiniCssExtractPlugin({138 filename: '[name].[contenthash].css'139 }),140 141 // TypeScript类型检查142 new ForkTsCheckerWebpackPlugin({143 typescript: {144 configFile: 'tsconfig.build.json'145 }146 })147 ],148 149 optimization: {150 splitChunks: {151 chunks: 'all',152 cacheGroups: {153 vendor: {154 test: /[\\/]node_modules[\\/]/,155 name: 'vendors',156 chunks: 'all'157 }158 }159 }160 }161}162163export default config164165// jest.config.ts - 测试配置166import type { Config } from 'jest'167168const config: Config = {169 preset: 'ts-jest',170 testEnvironment: 'jsdom',171 172 // 模块解析173 moduleNameMapping: {174 '^@/(.*)$': '<rootDir>/src/$1'175 },176 177 // 设置文件178 setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],179 180 // 覆盖率配置181 collectCoverageFrom: [182 'src/**/*.{ts,tsx}',183 '!src/**/*.d.ts',184 '!src/**/*.stories.{ts,tsx}',185 '!src/index.tsx'186 ],187 188 coverageThreshold: {189 global: {190 branches: 80,191 functions: 80,192 lines: 80,193 statements: 80194 }195 },196 197 // 转换配置198 transform: {199 '^.+\\.tsx?$': ['ts-jest', {200 tsconfig: 'tsconfig.json'201 }]202 },203 204 // 模块文件扩展名205 moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],206 207 // 测试匹配模式208 testMatch: [209 '<rootDir>/src/**/__tests__/**/*.{ts,tsx}',210 '<rootDir>/src/**/*.{test,spec}.{ts,tsx}'211 ]212}213214export default config215216// eslint.config.ts - ESLint配置217import { defineConfig } from 'eslint-define-config'218219export default defineConfig({220 root: true,221 env: {222 browser: true,223 es2020: true,224 node: true225 },226 227 extends: [228 'eslint:recommended',229 '@typescript-eslint/recommended',230 '@typescript-eslint/recommended-requiring-type-checking',231 'plugin:react/recommended',232 'plugin:react-hooks/recommended',233 'plugin:jsx-a11y/recommended'234 ],235 236 parser: '@typescript-eslint/parser',237 parserOptions: {238 ecmaVersion: 'latest',239 sourceType: 'module',240 project: ['./tsconfig.json'],241 tsconfigRootDir: __dirname,242 ecmaFeatures: {243 jsx: true244 }245 },246 247 plugins: [248 '@typescript-eslint',249 'react',250 'react-hooks',251 'jsx-a11y'252 ],253 254 rules: {255 // TypeScript规则256 '@typescript-eslint/no-unused-vars': 'error',257 '@typescript-eslint/no-explicit-any': 'warn',258 '@typescript-eslint/explicit-function-return-type': 'off',259 '@typescript-eslint/explicit-module-boundary-types': 'off',260 '@typescript-eslint/no-non-null-assertion': 'warn',261 '@typescript-eslint/prefer-nullish-coalescing': 'error',262 '@typescript-eslint/prefer-optional-chain': 'error',263 264 // React规则265 'react/react-in-jsx-scope': 'off',266 'react/prop-types': 'off',267 'react-hooks/rules-of-hooks': 'error',268 'react-hooks/exhaustive-deps': 'warn',269 270 // 通用规则271 'no-console': 'warn',272 'no-debugger': 'error',273 'prefer-const': 'error',274 'no-var': 'error'275 },276 277 settings: {278 react: {279 version: 'detect'280 }281 },282 283 ignorePatterns: [284 'dist',285 'build',286 'node_modules',287 '*.config.js'288 ]289})3. TypeScript最佳实践与模式
3.1 代码组织与架构模式
TypeScript的强类型特性为大型应用的架构设计提供了强有力的支持,合理的代码组织和架构模式至关重要。
架构模式对比
| 架构模式 | 适用规模 | 类型安全 | 维护成本 | 学习曲线 |
|---|---|---|---|---|
| MVC | 中小型 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| MVVM | 中型 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Clean Architecture | 大型 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Hexagonal | 大型 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Modular Monolith | 超大型 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
3.2 性能优化策略
TypeScript编译器提供了多种优化选项,合理配置可以显著提升编译性能和运行时性能。
编译性能优化检查清单
| 优化策略 | 配置项 | 性能提升 | 适用场景 |
|---|---|---|---|
| 增量编译 | incremental: true | ⭐⭐⭐⭐ | 开发环境 |
| 跳过库检查 | skipLibCheck: true | ⭐⭐⭐⭐⭐ | 大型项目 |
| 项目引用 | references | ⭐⭐⭐⭐ | 多包项目 |
| 类型导入 | import type | ⭐⭐⭐ | 类型密集项目 |
| 路径映射 | paths | ⭐⭐ | 深层嵌套项目 |
TypeScript最佳实践原则
- 类型优先:优先使用类型而不是any
- 渐进式采用:从JavaScript逐步迁移到TypeScript
- 严格模式:启用strict模式获得最佳类型安全
- 工具集成:充分利用IDE和构建工具的TypeScript支持
- 性能监控:定期检查编译性能和包大小
- 团队规范:建立统一的TypeScript编码规范
TypeScript作为现代前端开发的重要工具,其强大的类型系统、丰富的语言特性和完善的工具链为大型应用开发提供了坚实的基础。通过掌握TypeScript的核心概念、工程化配置和最佳实践,可以显著提升代码质量、开发效率和团队协作水平。
评论