Skip to content
On this page

TypeScript 最佳实践

TypeScript 最佳实践旨在帮助开发者编写更安全、更可维护、更高效的代码。

类型定义最佳实践

明确的类型定义

typescript
// ❌ 不好的做法 - 过于宽泛的类型
function processUser(user: any) {
  return user.name.toUpperCase();
}

// ✅ 好的做法 - 明确的接口定义
interface User {
  id: number;
  name: string;
  email?: string;
  createdAt: Date;
}

function processUser(user: User) {
  return user.name.toUpperCase();
}

使用联合类型而非 any

typescript
// ❌ 不好的做法
function formatValue(value: any) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  if (typeof value === 'number') {
    return value.toFixed(2);
  }
  return String(value);
}

// ✅ 好的做法
type FormatValue = string | number | boolean;

function formatValue(value: FormatValue) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  if (typeof value === 'number') {
    return value.toFixed(2);
  }
  return String(value);
}

避免类型断言

typescript
// ❌ 不好的做法
function getLength(input: any) {
  return (input as string).length;
}

// ✅ 好的做法 - 使用类型守卫
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function getLength(input: unknown) {
  if (isString(input)) {
    return input.length;
  }
  return 0;
}

接口与类型别名的使用

何时使用接口,何时使用类型别名

typescript
// 使用接口定义对象形状
interface User {
  id: number;
  name: string;
}

// 使用类型别名定义联合类型
type UserRole = 'admin' | 'user' | 'guest';

// 使用类型别名定义函数类型
type EventHandler<T> = (event: T) => void;

// 接口可以被扩展
interface AdminUser extends User {
  role: 'admin';
  permissions: string[];
}

接口的合并

typescript
// 接口可以被多次定义并自动合并
interface Window {
  myCustomProperty: string;
}

interface Window {
  anotherProperty: number;
}

// 结果等同于:
// interface Window {
//   myCustomProperty: string;
//   anotherProperty: number;
// }

泛型的最佳实践

限制泛型约束

typescript
// ❌ 不好的做法 - 过于宽松的约束
function getProperty<T>(obj: T, key: any) {
  return obj[key];
}

// ✅ 好的做法 - 使用 keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const user = { id: 1, name: 'John', age: 30 };
const userName = getProperty(user, 'name'); // 类型安全

泛型工具类型的使用

typescript
interface User {
  id: number;
  name: string;
  email: string;
  role: string;
  createdAt: Date;
}

// 使用 Partial 让所有属性变为可选
type UpdateUserInput = Partial<User>;

// 使用 Pick 选择特定属性
type PublicUser = Pick<User, 'id' | 'name'>;

// 使用 Omit 忽略特定属性
type UserWithoutSecrets = Omit<User, 'role' | 'createdAt'>;

// 使用 Exclude 从联合类型中排除某些类型
type Status = 'active' | 'inactive' | 'pending' | 'deleted';
type ActiveStatus = Exclude<Status, 'deleted'>; // 'active' | 'inactive' | 'pending'

函数最佳实践

明确的返回类型

typescript
// ❌ 不好的做法 - 依赖类型推断
const add = (a, b) => {
  return a + b;
}

// ✅ 好的做法 - 明确指定类型
const add = (a: number, b: number): number => {
  return a + b;
}

// 或者使用函数声明
function multiply(a: number, b: number): number {
  return a * b;
}

使用函数重载

typescript
// 函数重载提供更精确的类型信息
function format(value: string): string;
function format(value: number): string;
function format(value: boolean): string;
function format(value: string | number | boolean): string {
  if (typeof value === 'string') {
    return value.trim();
  }
  if (typeof value === 'number') {
    return value.toString();
  }
  return value ? 'true' : 'false';
}

类的最佳实践

明确的访问修饰符

typescript
class UserService {
  // 明确指定访问级别
  private users: User[] = [];
  protected readonly maxRetries: number = 3;
  public readonly baseUrl: string;

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

  // 使用访问器
  public getUser(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }

  private validateUser(user: User): boolean {
    return user.name.length > 0;
  }
}

依赖注入

typescript
interface Logger {
  log(message: string): void;
}

interface Database {
  save<T>(data: T): Promise<void>;
}

class UserService {
  constructor(
    private readonly logger: Logger,
    private readonly database: Database
  ) {}

  async createUser(userData: Partial<User>): Promise<User> {
    this.logger.log('Creating user...');
    const user = new User(userData);
    await this.database.save(user);
    this.logger.log('User created successfully');
    return user;
  }
}

异步编程最佳实践

类型安全的异步处理

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

// 类型安全的 API 调用
async function fetchUser(id: number): Promise<ApiResponse<User>> {
  try {
    const response = await fetch(`/api/users/${id}`);
    const result: ApiResponse<User> = await response.json();
    return result;
  } catch (error) {
    throw new Error(`Failed to fetch user: ${error.message}`);
  }
}

// 使用
async function handleUserFetch() {
  try {
    const result = await fetchUser(1);
    console.log(result.data.name); // 类型安全访问
  } catch (error) {
    console.error(error.message);
  }
}

Promise 的类型处理

typescript
// 使用 Promise.all 的类型安全方式
async function fetchMultipleUsers(ids: number[]): Promise<User[]> {
  const promises = ids.map(id => fetchUser(id));
  const results = await Promise.all(promises);
  return results.map(result => result.data);
}

// 处理 Promise.allSettled
async function fetchWithErrors(ids: number[]): Promise<{
  success: User[];
  failures: Error[];
}> {
  const promises = ids.map(id => fetchUser(id));
  const results = await Promise.allSettled(promises);
  
  const success: User[] = [];
  const failures: Error[] = [];
  
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      success.push(result.value.data);
    } else {
      failures.push(new Error(`Failed to fetch user ${ids[index]}: ${result.reason}`));
    }
  });
  
  return { success, failures };
}

配置最佳实践

tsconfig.json 配置

json
{
  "compilerOptions": {
    /* 基本配置 */
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    
    /* 严格类型检查 */
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    
    /* 额外检查 */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    
    /* 模块解析 */
    "moduleResolution": "node",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    },
    
    /* 生成选项 */
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    
    /* 实验性功能 */
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}

代码组织最佳实践

模块组织

typescript
// types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export type UserRole = 'admin' | 'user' | 'guest';

// services/userService.ts
import type { User, UserRole } from '../types/user';

export class UserService {
  async getUser(id: number): Promise<User> {
    // 实现
  }
  
  async updateUser(id: number, updates: Partial<User>): Promise<User> {
    // 实现
  }
}

// utils/validators.ts
export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

// constants/user.ts
export const USER_ROLES: UserRole[] = ['admin', 'user', 'guest'];
export const MAX_LOGIN_ATTEMPTS = 3;

命名约定

typescript
// 接口使用 PascalCase
interface UserConfig {
  name: string;
  age: number;
}

// 类型别名使用 PascalCase
type UserId = string | number;

// 函数使用 camelCase
function calculateTotal(items: number[]): number {
  return items.reduce((sum, item) => sum + item, 0);
}

// 常量使用 UPPER_SNAKE_CASE
const MAX_RETRY_ATTEMPTS = 3;
const DEFAULT_TIMEOUT = 5000;

// 泛型参数通常使用单个大写字母
function identity<T>(value: T): T {
  return value;
}

// Promise 返回函数使用 async 作为前缀或返回类型明确标识
async function fetchUserData(): Promise<User> {
  // 实现
}

错误处理最佳实践

自定义错误类型

typescript
export class ValidationError extends Error {
  constructor(public field: string, public value: any, message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

export class NetworkError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
    this.name = 'NetworkError';
  }
}

// 使用自定义错误
function validateUser(user: Partial<User>): asserts user is User {
  if (!user.name) {
    throw new ValidationError('name', user.name, 'Name is required');
  }
  if (!user.email) {
    throw new ValidationError('email', user.email, 'Email is required');
  }
}

类型安全的错误处理

typescript
// Result 类型模式
type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };

async function safeFetchUser(id: number): Promise<Result<User, Error>> {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      return { 
        success: false, 
        error: new Error(`HTTP ${response.status}: ${response.statusText}`) 
      };
    }
    const user: User = await response.json();
    return { success: true, data: user };
  } catch (error) {
    return { success: false, error: error as Error };
  }
}

// 使用
async function handleUser() {
  const result = await safeFetchUser(1);
  if (result.success) {
    console.log(result.data.name); // 类型安全
  } else {
    console.error(result.error.message);
  }
}

性能最佳实践

类型级别的性能优化

typescript
// 使用 const 断言来获得更具体的类型
const colors = ['red', 'green', 'blue'] as const;
// 类型为 readonly ['red', 'green', 'blue']
// 而不是 string[]

type Color = typeof colors[number]; 
// 类型为 'red' | 'green' | 'blue'

// 使用映射类型优化
interface ApiResponse {
  userId: number;
  userName: string;
  userEmail: string;
  userRole: string;
}

// 使用 Omit 重用类型定义
type UserUpdatePayload = Omit<ApiResponse, 'userId'>;

// 使用 Pick 精确选择需要的字段
type PublicUserData = Pick<ApiResponse, 'userName' | 'userRole'>;

测试最佳实践

类型安全的测试

typescript
// 使用 Jest 进行类型安全测试
import { describe, it, expect } from '@jest/globals';
import { UserService } from './userService';
import type { User } from './types/user';

describe('UserService', () => {
  let userService: UserService;

  beforeEach(() => {
    userService = new UserService();
  });

  it('should create a valid user', () => {
    const userData: Partial<User> = {
      name: 'John Doe',
      email: 'john@example.com'
    };

    const user = userService.createUser(userData);

    expect(user.name).toBe('John Doe');
    expect(user.email).toBe('john@example.com');
    expect(typeof user.id).toBe('number');
  });

  it('should validate user email', () => {
    const invalidUserData: Partial<User> = {
      name: 'John Doe',
      email: 'invalid-email'
    };

    expect(() => {
      userService.createUser(invalidUserData);
    }).toThrow(ValidationError);
  });
});

小结

TypeScript 最佳实践涵盖了类型定义、代码组织、错误处理、性能优化等多个方面。遵循这些实践可以帮助我们编写更安全、更可维护的代码。关键是要保持类型安全、使用适当的工具、遵循一致的命名约定,并在团队中建立统一的编码标准。