前言

TypeScript 已经成为现代前端开发的标配,它为 JavaScript 添加了静态类型检查,大大提升了代码的可维护性和开发效率。然而,仅仅使用 TypeScript 是不够的,掌握最佳实践才能真正发挥它的威力。

类型定义最佳实践

1. 优先使用类型而非接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ✅ 推荐:使用 type
type User = {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
};

// ❌ 避免:除非需要继承或声明合并
interface IUser {
  id: number;
  name: string;
  email: string;
}

2. 合理使用泛型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// ✅ 良好的泛型设计
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

async function fetchUser<T>(url: string): Promise<ApiResponse<T>> {
  const response = await fetch(url);
  return response.json();
}

// 使用示例
type User = { id: number; name: string };
const userResponse = await fetchUser<User>('/api/user/1');

3. 使用联合类型和字面量类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 定义状态类型
type Status = 'pending' | 'success' | 'error';

type ButtonProps = {
  variant: 'primary' | 'secondary' | 'danger';
  size: 'small' | 'medium' | 'large';
  onClick?: () => void;
};

// 使用
const MyButton: React.FC<ButtonProps> = ({ variant, size, onClick }) => {
  // 实现...
};

高级类型技巧

1. 条件类型

1
2
3
4
5
6
7
8
// 根据类型决定返回类型
type ApiResponse<T> = T extends string
  ? { message: T }
  : { data: T };

// 使用示例
type StringResponse = ApiResponse<string>; // { message: string }
type DataResponse = ApiResponse<{ id: number }>; // { data: { id: number } }

2. 映射类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建所有属性为可选的类型
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 创建所有属性为只读的类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

// 自定义映射类型
type StringifyProperties<T> = {
  [K in keyof T]: string;
};

type User = {
  id: number;
  name: string;
  age: number;
};

type StringifiedUser = StringifyProperties<User>;
// { id: string; name: string; age: string; }

3. 模板字面量类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 动态生成属性名
type EventNames = `on${Capitalize<string>}`;
type UserEventNames = EventNames & `${string}UserChanged`;

// 使用示例
const events: Record<UserEventNames, Function> = {
  onUserChanged: () => {},
  onUserDeleted: () => {},
  onUserCreated: () => {}
};

工程化配置

1. 严格的 tsconfig.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": false,
    "noUncheckedIndexedAccess": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

2. ESLint 集成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// .eslintrc.js
module.exports = {
  extends: [
    '@typescript-eslint/recommended',
    '@typescript-eslint/recommended-requiring-type-checking'
  ],
  rules: {
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/explicit-function-return-type': 'warn',
    '@typescript-eslint/no-explicit-any': 'warn',
    '@typescript-eslint/prefer-nullish-coalescing': 'error',
    '@typescript-eslint/prefer-optional-chain': 'error'
  }
};

常见陷阱和解决方案

1. 类型断言滥用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ❌ 避免:过度使用类型断言
const user = response as User;

// ✅ 推荐:类型守卫
function isUser(obj: any): obj is User {
  return typeof obj.id === 'number' &&
         typeof obj.name === 'string' &&
         typeof obj.email === 'string';
}

if (isUser(response)) {
  // TypeScript 知道这里 response 是 User 类型
  console.log(response.name);
}

2. any 类型的替代方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ 避免:使用 any
function processData(data: any) {
  return data.map((item: any) => item.name);
}

// ✅ 推荐:使用泛型
function processData<T extends { name: string }>(data: T[]): string[] {
  return data.map(item => item.name);
}

// 或者使用 unknown
function processUnknownData(data: unknown) {
  if (Array.isArray(data)) {
    return data.filter((item): item is { name: string } =>
      typeof item === 'object' && item !== null && 'name' in item
    ).map(item => item.name);
  }
  return [];
}

3. 处理异步代码

 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
// ✅ 良好的异步类型定义
type FetchOptions = {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  headers?: Record<string, string>;
  body?: unknown;
};

async function apiRequest<T>(
  url: string,
  options: FetchOptions = {}
): Promise<{ data: T; success: boolean }> {
  const response = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...options.headers
    },
    body: options.body ? JSON.stringify(options.body) : undefined
  });

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

  const data = await response.json();
  return { data, success: true };
}

// 使用示例
type User = { id: number; name: string };
const { data: user, success } = await apiRequest<User>('/api/user');

性能优化

1. 类型推断优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// ❌ 避免复杂的类型计算
type ComplexType<T> = {
  [K in keyof T]: T[K] extends string
    ? T[K] extends `${infer Prefix}_${infer Suffix}`
      ? { prefix: Prefix; suffix: Suffix }
      : T[K]
    : never;
};

// ✅ 简化类型定义
interface User {
  firstName: string;
  lastName: string;
  email: string;
}

type UserNameParts = {
  first: string;
  last: string;
};

2. 模块化类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export type UserDto = Omit<User, 'id'> & {
  password: string;
};

// types/api.ts
import type { User } from './user';

export interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

export type UserResponse = ApiResponse<User>;

测试和类型

1. 类型安全的测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 使用类型断言进行测试
import { expect, TypeOf } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email()
});

type User = TypeOf<typeof UserSchema>;

// 在测试中使用
test('user validation', () => {
  const userData = { id: 1, name: 'John', email: 'john@example.com' };
  const result = UserSchema.safeParse(userData);

  expect(result.success).toBe(true);
  if (result.success) {
    // result.data 的类型被正确推断
    expectTypeOf(result.data).toEqualTypeOf<User>();
  }
});

总结

TypeScript 的最佳实践可以帮助我们构建更安全、更可维护的应用:

  1. 类型优先:在编写代码前先定义好类型
  2. 严格模式:启用所有严格的类型检查选项
  3. 避免类型断言:优先使用类型守卫和类型推断
  4. 模块化设计:合理组织类型定义和接口
  5. 持续学习:关注 TypeScript 新特性和最佳实践

通过遵循这些最佳实践,我们可以充分发挥 TypeScript 的优势,构建高质量的 TypeScript 应用。


相关工具: