TypeScript 最佳实践指南


为什么使用 TypeScript?

TypeScript 为 JavaScript 添加了静态类型系统,在开发阶段就能发现潜在错误,提高代码质量和开发效率。

核心最佳实践

1. 启用严格模式

tsconfig.json 中启用所有严格检查:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true
  }
}

2. 优先使用接口和类型别名

// ✅ 好的做法
interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}

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

// ❌ 避免使用 any
function getUser(): any { // 不好
  return { id: '1', name: 'John' };
}

// ✅ 明确的返回类型
function getUser(): User {
  return {
    id: '1',
    name: 'John',
    email: 'john@example.com',
    createdAt: new Date()
  };
}

3. 使用联合类型和类型守卫

type Result<T> =
  | { success: true; data: T }
  | { success: false; error: string };

function processResult<T>(result: Result<T>) {
  // 类型守卫
  if (result.success) {
    console.log(result.data); // TypeScript 知道这里有 data
  } else {
    console.error(result.error); // TypeScript 知道这里有 error
  }
}

4. 善用泛型

// 泛型函数
function identity<T>(value: T): T {
  return value;
}

// 泛型接口
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

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

5. 使用工具类型

TypeScript 提供了许多实用的工具类型:

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

// Partial - 所有属性可选
type PartialTodo = Partial<Todo>;

// Required - 所有属性必需
type RequiredTodo = Required<PartialTodo>;

// Pick - 选择部分属性
type TodoPreview = Pick<Todo, 'title' | 'completed'>;

// Omit - 排除部分属性
type TodoInfo = Omit<Todo, 'completed'>;

// Record - 创建对象类型
type PageInfo = Record<'home' | 'about' | 'contact', { title: string }>;

6. 枚举的正确使用

// ✅ 字符串枚举(推荐)
enum Status {
  Pending = 'PENDING',
  InProgress = 'IN_PROGRESS',
  Completed = 'COMPLETED',
  Cancelled = 'CANCELLED'
}

// 或者使用 const 对象(更轻量)
const Status = {
  Pending: 'PENDING',
  InProgress: 'IN_PROGRESS',
  Completed: 'COMPLETED',
  Cancelled: 'CANCELLED'
} as const;

type StatusType = typeof Status[keyof typeof Status];

7. 异步代码类型安全

// Promise 类型
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  return data as User;
}

// 错误处理
type AsyncResult<T> = Promise<
  | { success: true; data: T }
  | { success: false; error: Error }
>;

async function safelyFetchUser(id: string): AsyncResult<User> {
  try {
    const user = await fetchUser(id);
    return { success: true, data: user };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error : new Error('Unknown error')
    };
  }
}

8. 只读属性和不可变数据

interface Config {
  readonly apiUrl: string;
  readonly timeout: number;
  readonly retries: number;
}

// ReadonlyArray
function sum(numbers: ReadonlyArray<number>): number {
  return numbers.reduce((a, b) => a + b, 0);
}

// Readonly 工具类型
type ReadonlyUser = Readonly<User>;

高级技巧

条件类型

type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

// 实用示例
type NonNullable<T> = T extends null | undefined ? never : T;

映射类型

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

type NullableUser = Nullable<User>;
// { id: string | null; name: string | null; ... }

模板字面量类型

type EventName = 'click' | 'scroll' | 'mousemove';
type EventListener = `on${Capitalize<EventName>}`;
// 'onClick' | 'onScroll' | 'onMousemove'

常见陷阱

1. 类型断言滥用

// ❌ 危险的做法
const user = response.data as User;

// ✅ 更安全的方式
function isUser(obj: any): obj is User {
  return (
    typeof obj.id === 'string' &&
    typeof obj.name === 'string' &&
    typeof obj.email === 'string'
  );
}

const data = response.data;
if (isUser(data)) {
  // 类型安全
  console.log(data.name);
}

2. 忽略可能的 null/undefined

// ❌ 可能出错
function getUserName(user: User | null): string {
  return user.name; // 错误!user 可能为 null
}

// ✅ 正确处理
function getUserName(user: User | null): string {
  return user?.name ?? 'Anonymous';
}

工具推荐

  • ESLint + @typescript-eslint: 代码规范检查
  • Prettier: 代码格式化
  • ts-node: 直接运行 TypeScript
  • type-coverage: 检查类型覆盖率

总结

TypeScript 不仅仅是添加类型,更重要的是培养类型思维。通过遵循这些最佳实践,你可以写出更安全、更易维护的代码。

记住:好的类型设计能让 bug 在编译阶段就被发现!