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