typescript-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript Best Practices

TypeScript 最佳实践

Quick Start

快速入门

Always enable strict mode and use explicit types for public APIs. Prefer type-only imports (
import type
) and named exports over default exports. Use discriminated unions for state management and type guards for runtime validation.
始终启用严格模式,并为公共API使用显式类型。优先使用仅类型导入(
import type
)和命名导出而非默认导出。使用判别式联合进行状态管理,使用类型守卫进行运行时验证。

Type Safety Fundamentals

类型安全基础

Strict Mode Configuration

严格模式配置

Enable all strict flags in
tsconfig.json
:
json
{
  "strict": true,
  "noImplicitAny": true,
  "noImplicitReturns": true,
  "strictNullChecks": true,
  "strictFunctionTypes": true
}
tsconfig.json
中启用所有严格标志:
json
{
  "strict": true,
  "noImplicitAny": true,
  "noImplicitReturns": true,
  "strictNullChecks": true,
  "strictFunctionTypes": true
}

Null Safety

空值安全

typescript
// Explicit null handling
export function findById<T>(items: Map<string, T>, id: string): T | null {
  return items.get(id) ?? null;
}

// Optional chaining and nullish coalescing
export function getName(item?: Item): string {
  return item?.name ?? 'Unknown';
}
typescript
// 显式空值处理
export function findById<T>(items: Map<string, T>, id: string): T | null {
  return items.get(id) ?? null;
}

// 可选链和空值合并
export function getName(item?: Item): string {
  return item?.name ?? 'Unknown';
}

Explicit Return Types

显式返回类型

Use explicit return types for public APIs:
typescript
// Good
export function validateConfig(data: unknown): ValidatedConfig | null {
  const result = ConfigSchema.safeParse(data);
  return result.success ? result.data : null;
}

// Bad - implicit return type
export function validateConfig(data) {
  return data;
}
为公共API使用显式返回类型:
typescript
// 推荐写法
export function validateConfig(data: unknown): ValidatedConfig | null {
  const result = ConfigSchema.safeParse(data);
  return result.success ? result.data : null;
}

// 不推荐写法 - 隐式返回类型
export function validateConfig(data) {
  return data;
}

Core Patterns

核心模式

Discriminated Unions for State

用于状态管理的判别式联合

typescript
export type ConnectionState =
  | { status: 'disconnected' }
  | { status: 'connecting'; progress: number }
  | { status: 'connected'; connectionId: string }
  | { status: 'error'; message: string };

// TypeScript knows which properties are available in each branch
typescript
export type ConnectionState =
  | { status: 'disconnected' }
  | { status: 'connecting'; progress: number }
  | { status: 'connected'; connectionId: string }
  | { status: 'error'; message: string };

// TypeScript 会知晓每个分支中可用的属性

Result Type for Error Handling

用于错误处理的结果类型

typescript
export type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

const result = await connectToService(options);
if (result.success) {
  console.log('Connected:', result.data.id);
} else {
  console.error('Failed:', result.error.message);
}
typescript
export type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

const result = await connectToService(options);
if (result.success) {
  console.log('已连接:', result.data.id);
} else {
  console.error('连接失败:', result.error.message);
}

Readonly Properties

只读属性

Use
readonly
for immutable data:
typescript
export interface Config {
  readonly enableFeature: boolean;
  readonly port: number;
}
为不可变数据使用
readonly
typescript
export interface Config {
  readonly enableFeature: boolean;
  readonly port: number;
}

Type Guards and Assertions

类型守卫与断言

typescript
// Type guard
export function isUserData(data: unknown): data is UserData {
  return typeof data === 'object' && data !== null && 'name' in data;
}

// Assertion function
export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
  if (value === undefined || value === null) throw new Error('Value is undefined or null');
}
typescript
// 类型守卫
export function isUserData(data: unknown): data is UserData {
  return typeof data === 'object' && data !== null && 'name' in data;
}

// 断言函数
export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
  if (value === undefined || value === null) throw new Error('值为undefined或null');
}

Import/Export Conventions

导入/导出约定

typescript
// Type-only imports (preferred)
import type { Config } from '../types/config';

// Named exports (avoid default exports)
export class DataService {}
export const getService = () => DataService.getInstance();

// Grouped imports
import { External } from 'external';           // External deps
import { internalUtil } from './utils';         // Internal utils
import type { MyType } from './types';          // Types
typescript
// 仅类型导入(推荐)
import type { Config } from '../types/config';

// 命名导出(避免默认导出)
export class DataService {}
export const getService = () => DataService.getInstance();

// 分组导入
import { External } from 'external';           // 外部依赖
import { internalUtil } from './utils';         // 内部工具
import type { MyType } from './types';          // 类型

When to Use References

何时使用参考文档

Reference FileWhen to Load
tsconfig.md
Setting up TypeScript configuration
interfaces-types.md
Defining interfaces, generic types, extending external types
unions-discriminated.md
Working with discriminated unions, result types, exhaustive checks
type-guards-assertions.md
Runtime validation, branded types, assertion functions
utility-types.md
Using built-in utilities, const assertions, template literal types
error-handling.md
Implementing structured errors, error factories, retry logic
generics.md
Building generic services, repositories, factories, event emitters
import-export.md
Organizing imports/exports, avoiding circular dependencies
参考文件适用场景
tsconfig.md
设置TypeScript配置时
interfaces-types.md
定义接口、泛型类型、扩展外部类型时
unions-discriminated.md
处理判别式联合、结果类型、穷尽性检查时
type-guards-assertions.md
运行时验证、品牌类型、断言函数时
utility-types.md
使用内置工具、const断言、模板字面量类型时
error-handling.md
实现结构化错误、错误工厂、重试逻辑时
generics.md
构建泛型服务、仓库、工厂、事件发射器时
import-export.md
组织导入/导出、避免循环依赖时

Common Anti-Patterns to Avoid

需避免的常见反模式

  1. Using
    any
    - Use
    unknown
    with type guards instead
  2. Default exports - Harder to refactor and tree-shake
  3. Implicit return types on public APIs
  4. Non-readonly interfaces for immutable data
  5. Nested optionals (
    { a?: { b?: string } }
    ) - use discriminated unions instead
  1. 使用
    any
    - 改用
    unknown
    搭配类型守卫
  2. 默认导出 - 更难重构和摇树优化
  3. 公共API使用隐式返回类型
  4. 不可变数据使用非只读接口
  5. 嵌套可选类型
    { a?: { b?: string } }
    )- 改用判别式联合