# TypeScript进阶教程:从熟练到精通

TypeScript进阶教程:从熟练到精通

🚀 前言

TypeScript已经成为现代前端开发不可或缺的工具,但很多开发者仅仅停留在基础类型注解的使用层面。本文将带你深入TypeScript的高级特性,掌握在实际项目中提升代码质量和开发效率的关键技巧。

💡 一、高级类型系统

1.1 条件类型(Conditional Types)

条件类型是TypeScript中强大的类型编程工具,它允许我们根据条件表达式创建动态类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 基础条件类型
type IsString<T> = T extends string ? true : false;

type Test1 = IsString<'hello'>; // true
type Test2 = IsString<123>; // false

// 从联合类型中过滤特定类型
type FilterString<T> = T extends string ? T : never;

type Mixed = string | number | boolean;
type StringsOnly = FilterString<Mixed>; // string

// 提取函数返回类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser() {
return { name: 'John', age: 30 };
}

type User = MyReturnType<typeof getUser>; // { name: string; age: number }

1.2 映射类型(Mapped Types)

映射类型允许我们基于现有类型创建新类型,这在处理对象类型时特别有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

type Partial<T> = {
[P in keyof T]?: T[P];
};

interface User {
id: number;
name: string;
email: string;
}

type ReadonlyUser = Readonly<User>;
// 等价于: { readonly id: number; readonly name: string; readonly email: string; }

// 高级映射:添加前缀
type AddPrefix<T, P extends string> = {
[K in keyof T as `${P}${Capitalize<string & K>}`]: T[K];
};

type PrefixedUser = AddPrefix<User, 'get'>;
// 等价于: { getId: number; getName: string; getEmail: string; }

1.3 模板字面量类型

TypeScript 4.1引入了模板字面量类型,使得字符串类型的操作更加灵活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 基础模板类型
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiEndpoint = `api/${string}`;

type FullEndpoint = `${HttpMethod} /${ApiEndpoint}`;

// 高级应用:自动生成API类型
type Routes = 'users' | 'posts' | 'comments';
type HttpMethods = 'get' | 'post' | 'put' | 'delete';

type ApiRoutes = {
[K in Routes as `${HttpMethods}${Capitalize<K>}`]: () => Promise<any>;
};

// 生成的类型:
// {
// getUsers: () => Promise<any>;
// postUsers: () => Promise<any>;
// putUsers: () => Promise<any>;
// deleteUsers: () => Promise<any>;
// // ... 其他路由
// }

二、装饰器深度应用

装饰器是TypeScript的实验性特性,但在很多框架中广泛使用。

2.1 类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function Controller(path: string) {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
routePath = path;
readonly isController = true;
};
};
}

function LogMethod(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;

descriptor.value = function (...args: any[]) {
console.log(`调用方法: ${propertyName}, 参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`方法 ${propertyName} 返回:`, result);
return result;
};
}

@Controller('/api/users')
class UserController {
private users: any[] = [];

@LogMethod
addUser(user: any) {
this.users.push(user);
return user;
}

@LogMethod
getUsers() {
return this.users;
}
}

2.2 参数装饰器与元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import 'reflect-metadata';

function Validate(min?: number, max?: number) {
return function (target: any, propertyKey: string, parameterIndex: number) {
const existingValidations = Reflect.getOwnMetadata('validations', target, propertyKey) || [];
existingValidations.push({ parameterIndex, min, max });
Reflect.defineMetadata('validations', existingValidations, target, propertyKey);
};
}

function validateParameters(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
const validations = Reflect.getOwnMetadata('validations', target, propertyName) || [];

descriptor.value = function (...args: any[]) {
for (const validation of validations) {
const value = args[validation.parameterIndex];

if (validation.min !== undefined && value < validation.min) {
throw new Error(`参数 ${validation.parameterIndex} 必须大于等于 ${validation.min}`);
}

if (validation.max !== undefined && value > validation.max) {
throw new Error(`参数 ${validation.parameterIndex} 必须小于等于 ${validation.max}`);
}
}

return originalMethod.apply(this, args);
};
}

class Calculator {
@validateParameters
multiply(@Validate(1) a: number, @Validate(1, 10) b: number) {
return a * b;
}
}

💡 三、高级泛型技巧

3.1 泛型约束与条件分发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;

type StringOrNumberArray = ToArray<string | number>; // string[] | number[]

// 排除特定类型
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;

// 函数重载与泛型
interface ApiResponse<T = any> {
data: T;
status: number;
message: string;
}

class ApiClient {
async request<T>(url: string): Promise<ApiResponse<T>>;
async request<T>(url: string, data: any): Promise<ApiResponse<T>>;
async request<T>(url: string, data?: any): Promise<ApiResponse<T>> {
const config: RequestInit = data ? {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
} : { method: 'GET' };

const response = await fetch(url, config);
const result = await response.json();

return {
data: result as T,
status: response.status,
message: response.statusText
};
}
}

3.2 递归类型与高级模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};

// 深度可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P]>
: T[P];
};

interface NestedObject {
level1: {
level2: {
level3: {
value: string;
};
};
items: number[];
};
}

type ReadonlyNested = DeepReadonly<NestedObject>;
type PartialNested = DeepPartial<NestedObject>;

// 路径提取
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;

type UserPaths = Paths<NestedObject>;
// "level1" | "level1.level2" | "level1.level2.level3" | "level1.level2.level3.value" | "level1.items"

✨ 四、命名空间与模块高级用法

4.1 命名空间合并与声明合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 声明合并
interface User {
id: number;
name: string;
}

// 后续声明会自动合并
interface User {
email: string;
age?: number;
}

// 命名空间合并
namespace API {
export interface Response<T> {
data: T;
success: boolean;
}
}

namespace API {
export interface Error {
code: number;
message: string;
}

export function handleError(error: Error): void {
console.error(`API Error ${error.code}: ${error.message}`);
}
}

// 使用合并后的命名空间
const response: API.Response<User> = {
data: { id: 1, name: 'John', email: '[email protected]' },
success: true
};

4.2 高级模块模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 工厂模式与单例
interface ServiceConfig {
endpoint: string;
timeout: number;
}

abstract class BaseService {
abstract initialize(config: ServiceConfig): void;
}

class UserService extends BaseService {
private static instance: UserService;
private config!: ServiceConfig;

private constructor() {
super();
}

static getInstance(): UserService {
if (!UserService.instance) {
UserService.instance = new UserService();
}
return UserService.instance;
}

initialize(config: ServiceConfig): void {
this.config = config;
}

async getUsers(): Promise<any[]> {
const response = await fetch(this.config.endpoint, {
signal: AbortSignal.timeout(this.config.timeout)
});
return response.json();
}
}

// 模块导出模式
export const createService = <T extends BaseService>(
ServiceClass: new () => T,
config: ServiceConfig
): T => {
const service = new ServiceClass();
service.initialize(config);
return service;
};

export { UserService };
export type { ServiceConfig };

五、性能优化与最佳实践

5.1 类型性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 避免过度使用枚举
// 不推荐:
enum Status {
Pending = 'pending',
Approved = 'approved',
Rejected = 'rejected'
}

// 推荐:
const Status = {
Pending: 'pending',
Approved: 'approved',
Rejected: 'rejected'
} as const;

type Status = typeof Status[keyof typeof Status];

// 使用 const assertions
const ROUTES = {
home: '/',
about: '/about',
contact: '/contact'
} as const;

type Route = keyof typeof ROUTES; // 'home' | 'about' | 'contact'

// 懒加载类型
type HeavyComputation<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;

// 使用 Opaque类型增强类型安全
declare const brand: unique symbol;

type Brand<T, B> = T & { [brand]: B };
type UserId = Brand<number, 'UserId'>;
type ProductId = Brand<number, 'ProductId'>;

function createUserId(id: number): UserId {
return id as UserId;
}

function createProductId(id: number): ProductId {
return id as ProductId;
}

// 这样就不会意外混淆不同类型的ID
const userId: UserId = createUserId(1);
const productId: ProductId = createProductId(1);

// 下面的代码会在编译时报错:
// const invalid: UserId = productId;

5.2 工程化配置技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// tsconfig.json 高级配置示例
{
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"]
},
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}

🌟 六、实战案例:构建类型安全的API客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 定义API类型
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

interface ApiDefinition {
path: string;
method: HttpMethod;
request?: any;
response: any;
}

// API配置
interface ApiConfig {
[key: string]: ApiDefinition;
}

const apiConfig = {
getUser: {
path: '/users/:id',
method: 'GET',
response: { id: 0, name: '', email: '' }
},
createUser: {
path: '/users',
method: 'POST',
request: { name: '', email: '' },
response: { id: 0, name: '', email: '' }
},
updateUser: {
path: '/users/:id',
method: 'PUT',
request: { name: '', email: '' },
response: { success: true }
}
} as const;

// 类型安全的API客户端
class TypedApiClient {
private baseURL: string;

constructor(baseURL: string) {
this.baseURL = baseURL;
}

async request<
K extends keyof typeof apiConfig,
T extends typeof apiConfig[K]
>(
key: K,
...args: T['request'] extends undefined
? [params?: Record<string, string>]
: [data: T['request'], params?: Record<string, string>]
): Promise<T['response']> {
const config = apiConfig[key];
let [data, params] = args as [any, Record<string, string>?];

if (params === undefined && data && !config.request) {
params = data;
data = undefined;
}

let url = config.path;
if (params) {
url = url.replace(/:(\w+)/g, (_, key) => params![key] || '');
}

const response = await fetch(`${this.baseURL}${url}`, {
method: config.method,
headers: data ? { 'Content-Type': 'application/json' } : {},
body: data ? JSON.stringify(data) : undefined
});

if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}

return response.json();
}
}

// 使用示例
const api = new TypedApiClient('https://api.example.com');

// 这些调用都是类型安全的
const user = await api.request('getUser', { id: '123' });
const newUser = await api.request('createUser', { name: 'John', email: '[email protected]' });
const result = await api.request('updateUser', { name: 'John Updated' }, { id: '123' });

👋 结语

通过本教程,我们深入探讨了TypeScript的高级特性,包括条件类型、映射类型、装饰器、高级泛型等。这些特性不仅能提升代码的类型安全性,还能显著提高开发效率和代码质量。掌握这些进阶技巧后,你将能够构建更加健壮、可维护的TypeScript应用程序。

记住,TypeScript的强大之处在于它的类型系统,合理利用这些高级特性,可以让你的代码在编译时就能发现潜在的错误,减少运行时的问题。继续实践和探索,你会发现TypeScript带来的更多价值。

[up主专用,视频内嵌代码贴在这]