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 在编译阶段就被发现!