howto-code-in-typescript

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript House Style

TypeScript团队编码风格指南

Overview

概述

Comprehensive TypeScript coding standards emphasizing type safety, immutability, and integration with Functional Core, Imperative Shell (FCIS) pattern.
Core principles:
  • Types as documentation and constraints
  • Immutability by default prevents bugs
  • Explicit over implicit (especially in function signatures)
  • Functional Core returns Results, Imperative Shell may throw
  • Configuration over decoration/magic
这份全面的TypeScript编码标准强调类型安全、不可变性,以及与Functional Core, Imperative Shell(FCIS)模式的集成。
核心原则:
  • 类型作为文档与约束
  • 默认不可变性以预防bug
  • 显式优于隐式(尤其在函数签名中)
  • 函数式核心返回Result类型,命令式外壳可抛出异常
  • 配置优于装饰器/魔法特性

Quick Self-Check (Use Under Pressure)

快速自检清单(紧急场景下使用)

When under deadline pressure or focused on other concerns (performance, accuracy, features), STOP and verify:
  • Using
    Array<T>
    not
    T[]
  • Using
    type
    not
    interface
    (unless class contract)
  • Using math.js for money/currencies/complex math
  • Parameters are
    readonly
    or
    Readonly<T>
  • Using
    unknown
    not
    any
  • Using
    null
    for absent values (not
    undefined
    )
  • Using function declarations (not const arrow) for top-level functions
  • Using named exports (not default exports)
  • Using
    ===
    not
    ==
  • Using
    .sort((a, b) => a - b)
    for numeric arrays
  • Using
    parseInt(x, 10)
    with explicit radix
Why this matters: Under pressure, you'll default to muscle memory. These checks catch the most common violations.
在截止日期临近或专注于其他事项(性能、准确性、功能开发)时,请停下来验证以下内容:
  • 使用
    Array<T>
    而非
    T[]
  • 使用
    type
    而非
    interface
    (除非是类契约)
  • 货币/金融/复杂数学计算使用math.js
  • 参数标记为
    readonly
    Readonly<T>
  • 使用
    unknown
    而非
    any
  • 缺失值使用
    null
    (而非
    undefined
  • 顶层函数使用
    function
    声明(而非箭头函数赋值)
  • 使用具名导出(而非默认导出)
  • 使用
    ===
    而非
    ==
  • 数值数组排序使用
    .sort((a, b) => a - b)
  • parseInt(x, 10)
    需显式指定基数
重要性: 紧急情况下,你会依赖肌肉记忆编写代码,这些检查能帮你捕获最常见的规范违规。

Type Declarations

类型声明

Type vs Interface

Type与Interface的选择

Always use
type
except for class contracts.
typescript
// GOOD: type for object shapes
type UserData = {
  readonly id: string;
  name: string;
  email: string | null;
};

// GOOD: interface for class contract
interface IUserRepository {
  findById(id: string): Promise<User | null>;
}

class UserRepository implements IUserRepository {
  // implementation
}

// BAD: interface for object shape
interface UserData {
  id: string;
  name: string;
}
Rationale: Types compose better with unions and intersections, support mapped types, and avoid declaration merging surprises. Interfaces are only for defining what a class must implement.
IMPORTANT: Even when under deadline pressure, even when focused on other concerns (financial accuracy, performance optimization, bug fixes), take 2 seconds to ask: "Is this a class contract?" If no, use
type
. Don't default to
interface
out of habit.
除非是定义类契约,否则始终使用
type
typescript
// 规范:用type定义对象结构
type UserData = {
  readonly id: string;
  name: string;
  email: string | null;
};

// 规范:用interface定义类契约
interface IUserRepository {
  findById(id: string): Promise<User | null>;
}

class UserRepository implements IUserRepository {
  // 实现代码
}

// 不规范:用interface定义对象结构
interface UserData {
  id: string;
  name: string;
}
理由: Type在联合类型、交叉类型的组合上表现更优,支持映射类型,还能避免声明合并带来的意外问题。Interface仅用于定义类必须实现的契约。
重点提示: 即使在紧急场景或专注于其他事项(财务准确性、性能优化、bug修复)时,也要花2秒问自己:“这是类契约吗?”如果不是,就用
type
,不要因为习惯默认使用
interface

Naming Conventions

命名规范

Type Suffixes

类型后缀

SuffixUsageExample
FooOptions
Function parameter objects (3+ args or any optional)
ProcessUserOptions
FooConfig
Persistent configuration from storage
DatabaseConfig
FooResult
Discriminated union return types
ValidationResult
FooFn
Function/callback types
TransformFn<T>
FooProps
React component props
ButtonProps
FooState
State objects (component/application)
AppState
后缀用途示例
FooOptions
函数参数对象(参数≥3个或包含可选参数)
ProcessUserOptions
FooConfig
从存储读取的持久化配置
DatabaseConfig
FooResult
可区分联合类型的返回值
ValidationResult
FooFn
函数/回调类型
TransformFn<T>
FooProps
React组件属性
ButtonProps
FooState
组件/应用状态对象
AppState

General Casing

通用大小写规则

ElementConventionExample
Variables & functionscamelCase
userName
,
getUser()
Types & classesPascalCase
UserData
,
UserService
ConstantsUPPER_CASE
MAX_RETRY_COUNT
,
API_ENDPOINT
Fileskebab-case
user-service.ts
,
process-order.ts
元素规范示例
变量与函数camelCase
userName
,
getUser()
类型与类PascalCase
UserData
,
UserService
常量UPPER_CASE
MAX_RETRY_COUNT
,
API_ENDPOINT
文件kebab-case
user-service.ts
,
process-order.ts

Boolean Naming

布尔值命名

Use is/has/can/should/will prefixes. Avoid negative names.
typescript
// GOOD
const isActive = true;
const hasPermission = checkPermission();
const canEdit = user.role === 'admin';
const shouldRetry = attempts < MAX_RETRIES;
const willTimeout = elapsed > threshold;

// Also acceptable: adjectives for state
type User = {
  active: boolean;
  visible: boolean;
  disabled: boolean;
};

// BAD: negative names
const isDisabled = false; // prefer isEnabled
const notReady = true;    // prefer isReady
使用is/has/can/should/will前缀,避免使用否定式命名。
typescript
// 规范
const isActive = true;
const hasPermission = checkPermission();
const canEdit = user.role === 'admin';
const shouldRetry = attempts < MAX_RETRIES;
const willTimeout = elapsed > threshold;

// 也可接受:用形容词表示状态
type User = {
  active: boolean;
  visible: boolean;
  disabled: boolean;
};

// 不规范:否定式命名
const isDisabled = false; // 优先使用isEnabled
const notReady = true;    // 优先使用isReady

Type Suffix Details

类型后缀细节

FooOptions - Parameter Objects

FooOptions - 参数对象

Use for functions with 3+ arguments OR any optional arguments.
typescript
type ProcessUserOptions = {
  readonly name: string;
  readonly email: string;
  readonly age: number;
  readonly sendWelcome?: boolean;
};

// GOOD: destructure in body, not in parameters
function processUser(options: ProcessUserOptions): void {
  const {name, email, age, sendWelcome = true} = options;
  // implementation
}

// BAD: inline destructuring in parameters
function processUser({name, email, age}: {name: string, email: string, age: number}) {
  // causes duplication when destructuring
}

// BAD: not using options pattern for 3+ args
function processUser(name: string, email: string, age: number, sendWelcome?: boolean) {
  // hard to call, positional arguments
}
当函数参数≥3个或包含可选参数时,使用参数对象。
typescript
type ProcessUserOptions = {
  readonly name: string;
  readonly email: string;
  readonly age: number;
  readonly sendWelcome?: boolean;
};

// 规范:在函数体内解构,而非参数中
function processUser(options: ProcessUserOptions): void {
  const {name, email, age, sendWelcome = true} = options;
  // 实现代码
}

// 不规范:参数中直接解构
function processUser({name, email, age}: {name: string, email: string, age: number}) {
  // 解构时会导致代码重复
}

// 不规范:参数≥3个时未使用options模式
function processUser(name: string, email: string, age: number, sendWelcome?: boolean) {
  // 调用困难,依赖参数位置
}

FooResult - Discriminated Unions

FooResult - 可区分联合类型

Always use discriminated unions for Result types. Integrate with neverthrow.
typescript
// GOOD: discriminated union with success/error
type ValidationResult =
  | { success: true; data: ValidUser }
  | { success: false; error: ValidationError };

// GOOD: use neverthrow for Result types
import {Result, ok, err} from 'neverthrow';

type ValidationError = {
  field: string;
  message: string;
};

function validateUser(data: Readonly<UserData>): Result<ValidUser, ValidationError> {
  if (!data.email) {
    return err({field: 'email', message: 'Email is required'});
  }
  return ok({...data, validated: true});
}

// Usage
const result = validateUser(userData);
if (result.isOk()) {
  console.log(result.value); // ValidUser
} else {
  console.error(result.error); // ValidationError
}
Rule: Functional Core functions should return
Result<T, E>
types. Imperative Shell functions may throw exceptions for HTTP errors and similar.
Result类型始终使用可区分联合类型,结合neverthrow库使用。
typescript
// 规范:带success/error标记的可区分联合类型
type ValidationResult =
  | { success: true; data: ValidUser }
  | { success: false; error: ValidationError };

// 规范:用neverthrow定义Result类型
import {Result, ok, err} from 'neverthrow';

type ValidationError = {
  field: string;
  message: string;
};

function validateUser(data: Readonly<UserData>): Result<ValidUser, ValidationError> {
  if (!data.email) {
    return err({field: 'email', message: '邮箱为必填项'});
  }
  return ok({...data, validated: true});
}

// 使用示例
const result = validateUser(userData);
if (result.isOk()) {
  console.log(result.value); // ValidUser类型
} else {
  console.error(result.error); // ValidationError类型
}
规则: 函数式核心函数应返回
Result<T, E>
类型。命令式外壳函数可针对HTTP错误等场景抛出异常。

Functions

函数

Declaration Style

声明风格

Use
function
declarations for top-level functions. Use arrow functions for inline callbacks.
typescript
// GOOD: function declaration for top-level
function processUser(data: Readonly<UserData>): ProcessResult {
  return {success: true, user: data};
}

// GOOD: arrow functions for inline callbacks
const users = rawData.map(u => transformUser(u));
button.addEventListener('click', (e) => handleClick(e));
fetch(url).then(data => processData(data));

// BAD: const arrow for top-level function
const processUser = (data: UserData): ProcessResult => {
  return {success: true, user: data};
};
Rationale: Function declarations are hoisted and more visible. Arrow functions capture lexical
this
and are concise for callbacks.
顶层函数使用
function
声明,内联回调使用箭头函数。
typescript
// 规范:顶层函数用function声明
function processUser(data: Readonly<UserData>): ProcessResult {
  return {success: true, user: data};
}

// 规范:内联回调用箭头函数
const users = rawData.map(u => transformUser(u));
button.addEventListener('click', (e) => handleClick(e));
fetch(url).then(data => processData(data));

// 不规范:顶层函数用箭头函数赋值
const processUser = (data: UserData): ProcessResult => {
  return {success: true, user: data};
};
理由: function声明会被提升,可读性更强。箭头函数会捕获词法作用域的
this
,且作为回调时更简洁。

Const Arrow Functions

箭头函数赋值

Use
const foo = () => {}
declarations only for stable references.
typescript
// GOOD: stable reference for React hooks
const handleSubmit = (event: FormEvent) => {
  event.preventDefault();
  // implementation
};

useEffect(() => {
  // handleSubmit reference is stable
}, [handleSubmit]);

// GOOD: long event listener passed from variable
const handleComplexClick = (event: MouseEvent) => {
  // many lines of logic
};
element.addEventListener('click', handleComplexClick);

// BAD: const arrow for regular top-level function
const calculateTotal = (items: Array<Item>): number => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

// GOOD: use function declaration
function calculateTotal(items: ReadonlyArray<Item>): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}
仅当需要稳定引用时,使用
const foo = () => {}
声明。
typescript
// 规范:React Hooks中使用稳定引用
const handleSubmit = (event: FormEvent) => {
  event.preventDefault();
  // 实现代码
};

useEffect(() => {
  // handleSubmit引用稳定
}, [handleSubmit]);

// 规范:复杂事件监听器用变量存储
const handleComplexClick = (event: MouseEvent) => {
  // 多行逻辑代码
};
element.addEventListener('click', handleComplexClick);

// 不规范:普通顶层函数用箭头函数
const calculateTotal = (items: Array<Item>): number => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

// 规范:使用function声明
function calculateTotal(items: ReadonlyArray<Item>): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

Parameter Objects

参数对象

Use parameter objects for 3+ arguments OR any optional arguments.
typescript
// GOOD: options object for 3+ args
type CreateUserOptions = {
  readonly name: string;
  readonly email: string;
  readonly age: number;
  readonly newsletter?: boolean;
};

function createUser(options: CreateUserOptions): User {
  const {name, email, age, newsletter = false} = options;
  // implementation
}

// GOOD: 2 args, but one is optional - use options
type SendEmailOptions = {
  readonly to: string;
  readonly subject: string;
  readonly body?: string;
};

function sendEmail(options: SendEmailOptions): void {
  // implementation
}

// GOOD: 2 required args - no options needed
function divide(numerator: number, denominator: number): number {
  return numerator / denominator;
}
当函数参数≥3个或包含可选参数时,使用参数对象。
typescript
// 规范:参数≥3个时用options对象
type CreateUserOptions = {
  readonly name: string;
  readonly email: string;
  readonly age: number;
  readonly newsletter?: boolean;
};

function createUser(options: CreateUserOptions): User {
  const {name, email, age, newsletter = false} = options;
  // 实现代码
}

// 规范:2个参数但含可选参数时用options对象
type SendEmailOptions = {
  readonly to: string;
  readonly subject: string;
  readonly body?: string;
};

function sendEmail(options: SendEmailOptions): void {
  // 实现代码
}

// 规范:2个必填参数,无需options对象
function divide(numerator: number, denominator: number): number {
  return numerator / denominator;
}

Async Functions

异步函数

Always explicitly type Promise returns. Avoid async void.
typescript
// GOOD: explicit Promise return type
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// GOOD: Promise<void> for side effects
async function saveUser(user: User): Promise<void> {
  await fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(user),
  });
}

// BAD: implicit return type
async function fetchUser(id: string) {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}
Prefer async/await over
.then()
chains.
typescript
// GOOD: async/await
async function processUserData(id: string): Promise<ProcessedUser> {
  const user = await fetchUser(id);
  const enriched = await enrichUserData(user);
  return transformUser(enriched);
}

// BAD: promise chains
function processUserData(id: string): Promise<ProcessedUser> {
  return fetchUser(id)
    .then(user => enrichUserData(user))
    .then(enriched => transformUser(enriched));
}
始终显式声明Promise返回类型,避免async void。
typescript
// 规范:显式声明Promise返回类型
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// 规范:副作用函数返回Promise<void>
async function saveUser(user: User): Promise<void> {
  await fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(user),
  });
}

// 不规范:返回类型隐式
async function fetchUser(id: string) {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}
优先使用async/await而非
.then()
链式调用。
typescript
// 规范:使用async/await
async function processUserData(id: string): Promise<ProcessedUser> {
  const user = await fetchUser(id);
  const enriched = await enrichUserData(user);
  return transformUser(enriched);
}

// 不规范:Promise链式调用
function processUserData(id: string): Promise<ProcessedUser> {
  return fetchUser(id)
    .then(user => enrichUserData(user))
    .then(enriched => transformUser(enriched));
}

When to Use Async

何时使用异步

Be selective with async. Not everything needs to be async. Sync code is simpler to reason about and debug.
Use async for:
  • Network requests, database operations, file I/O
  • Operations that benefit from concurrent execution (Promise.all)
  • External service calls
Stay sync for:
  • Pure calculations and transformations
  • Simple data structure operations
  • Code that doesn't touch external systems
typescript
// GOOD: sync for pure transformation
function transformUser(user: User): TransformedUser {
  return {
    fullName: `${user.firstName} ${user.lastName}`,
    email: user.email.toLowerCase(),
  };
}

// GOOD: async for I/O
async function loadAndTransformUser(id: string): Promise<TransformedUser> {
  const user = await fetchUser(id);
  return transformUser(user); // Sync call inside async function is fine
}

// BAD: unnecessary async
async function transformUser(user: User): Promise<TransformedUser> {
  return {
    fullName: `${user.firstName} ${user.lastName}`,
    email: user.email.toLowerCase(),
  };
}
Why this matters: Async adds complexity—error propagation, cleanup, and stack traces become harder to follow. Keep the async boundary as close to the I/O as possible.
谨慎使用异步,并非所有场景都需要。同步代码更易推理和调试。
使用异步的场景:
  • 网络请求、数据库操作、文件I/O
  • 可并发执行的操作(使用Promise.all)
  • 外部服务调用
保持同步的场景:
  • 纯计算与转换逻辑
  • 简单数据结构操作
  • 不涉及外部系统的代码
typescript
// 规范:纯转换逻辑用同步函数
function transformUser(user: User): TransformedUser {
  return {
    fullName: `${user.firstName} ${user.lastName}`,
    email: user.email.toLowerCase(),
  };
}

// 规范:I/O操作使用异步
async function loadAndTransformUser(id: string): Promise<TransformedUser> {
  const user = await fetchUser(id);
  return transformUser(user); // 异步函数内可调用同步函数
}

// 不规范:不必要的异步
async function transformUser(user: User): Promise<TransformedUser> {
  return {
    fullName: `${user.firstName} ${user.lastName}`,
    email: user.email.toLowerCase(),
  };
}
理由: 异步会增加复杂度——错误传播、资源清理和堆栈追踪都会变得更难处理。应将异步边界尽可能靠近I/O操作。

Classes

When to Use Classes

何时使用类

Prefer functions over classes, EXCEPT for dependency injection patterns.
typescript
// GOOD: class as dependency container
class UserService {
  constructor(
    private readonly db: Database,
    private readonly logger: Logger,
    private readonly cache: Cache,
  ) {}

  async getUser(id: string): Promise<User | null> {
    this.logger.info(`Fetching user ${id}`);
    const cached = await this.cache.get(`user:${id}`);
    if (cached) return cached;

    const user = await this.db.users.findById(id);
    if (user) await this.cache.set(`user:${id}`, user);
    return user;
  }
}

// BAD: class with no dependencies
class MathUtils {
  add(a: number, b: number): number {
    return a + b;
  }
}

// GOOD: plain functions
function add(a: number, b: number): number {
  return a + b;
}
优先使用函数而非类,除非是依赖注入场景。
typescript
// 规范:类作为依赖容器
class UserService {
  constructor(
    private readonly db: Database,
    private readonly logger: Logger,
    private readonly cache: Cache,
  ) {}

  async getUser(id: string): Promise<User | null> {
    this.logger.info(`获取用户 ${id}`);
    const cached = await this.cache.get(`user:${id}`);
    if (cached) return cached;

    const user = await this.db.users.findById(id);
    if (user) await this.cache.set(`user:${id}`, user);
    return user;
  }
}

// 不规范:无依赖的类
class MathUtils {
  add(a: number, b: number): number {
    return a + b;
  }
}

// 规范:普通函数
function add(a: number, b: number): number {
  return a + b;
}

Class Structure

类结构

Use constructor injection into private readonly fields.
typescript
// GOOD: constructor injection, private readonly
class OrderProcessor {
  constructor(
    private readonly orderRepo: OrderRepository,
    private readonly paymentService: PaymentService,
    private readonly notifier: NotificationService,
  ) {}

  async processOrder(orderId: string): Promise<void> {
    const order = await this.orderRepo.findById(orderId);
    // implementation
  }
}

// BAD: public mutable fields
class OrderProcessor {
  public orderRepo: OrderRepository;
  public paymentService: PaymentService;

  constructor(orderRepo: OrderRepository, paymentService: PaymentService) {
    this.orderRepo = orderRepo;
    this.paymentService = paymentService;
  }
}
使用构造函数注入私有只读字段。
typescript
// 规范:构造函数注入,私有只读字段
class OrderProcessor {
  constructor(
    private readonly orderRepo: OrderRepository,
    private readonly paymentService: PaymentService,
    private readonly notifier: NotificationService,
  ) {}

  async processOrder(orderId: string): Promise<void> {
    const order = await this.orderRepo.findById(orderId);
    // 实现代码
  }
}

// 不规范:公共可变字段
class OrderProcessor {
  public orderRepo: OrderRepository;
  public paymentService: PaymentService;

  constructor(orderRepo: OrderRepository, paymentService: PaymentService) {
    this.orderRepo = orderRepo;
    this.paymentService = paymentService;
  }
}

The 'this' Keyword

this关键字

Use
this
only in class methods. Avoid elsewhere.
typescript
// GOOD: this in class method
class Counter {
  private count = 0;

  increment(): void {
    this.count++;
  }
}

// BAD: this in object literal
const counter = {
  count: 0,
  increment() {
    this.count++; // fragile, breaks when passed as callback
  },
};

// GOOD: closure over variable
function createCounter() {
  let count = 0;
  return {
    increment: () => count++,
    getCount: () => count,
  };
}
仅在类方法中使用
this
,其他场景避免使用。
typescript
// 规范:类方法中使用this
class Counter {
  private count = 0;

  increment(): void {
    this.count++;
  }
}

// 不规范:对象字面量中使用this
const counter = {
  count: 0,
  increment() {
    this.count++; // 脆弱,作为回调传递时会失效
  },
};

// 规范:通过闭包捕获变量
function createCounter() {
  let count = 0;
  return {
    increment: () => count++,
    getCount: () => count,
  };
}

Type Inference

类型推断

When Inference is Acceptable

可接受的推断场景

Always explicit in function signatures. Infer in local variables, loops, destructuring, and intermediate calculations.
typescript
// GOOD: explicit function signature, inferred locals
function processUsers(users: ReadonlyArray<User>): Array<ProcessedUser> {
  const results: Array<ProcessedUser> = [];

  for (const user of users) { // user inferred as User
    const name = user.name; // name inferred as string
    const upper = name.toUpperCase(); // upper inferred as string
    const processed = {id: user.id, name: upper}; // processed inferred
    results.push(processed);
  }

  return results;
}

// GOOD: destructuring with inference
function formatUser({name, email}: User): string {
  return `${name} <${email}>`;
}

// BAD: missing return type
function processUsers(users: ReadonlyArray<User>) {
  // ...
}

// BAD: excessive annotations on locals
function processUsers(users: ReadonlyArray<User>): Array<ProcessedUser> {
  const results: Array<ProcessedUser> = [];

  for (const user: User of users) {
    const name: string = user.name;
    const upper: string = name.toUpperCase();
    // ...
  }

  return results;
}
函数签名始终显式声明类型,局部变量、循环、解构和中间计算可使用推断。
typescript
// 规范:函数签名显式,局部变量推断
function processUsers(users: ReadonlyArray<User>): Array<ProcessedUser> {
  const results: Array<ProcessedUser> = [];

  for (const user of users) { // user被推断为User类型
    const name = user.name; // name被推断为string类型
    const upper = name.toUpperCase(); // upper被推断为string类型
    const processed = {id: user.id, name: upper}; // processed类型自动推断
    results.push(processed);
  }

  return results;
}

// 规范:解构时使用推断
function formatUser({name, email}: User): string {
  return `${name} <${email}>`;
}

// 不规范:缺失返回类型
function processUsers(users: ReadonlyArray<User>) {
  // ...
}

// 不规范:局部变量过度注解
function processUsers(users: ReadonlyArray<User>): Array<ProcessedUser> {
  const results: Array<ProcessedUser> = [];

  for (const user: User of users) {
    const name: string = user.name;
    const upper: string = name.toUpperCase();
    // ...
  }

  return results;
}

Immutability

不可变性

Readonly by Default

默认只读

Mark reference type parameters as
Readonly<T>
. Use
const
for all bindings unless mutation needed.
typescript
// GOOD: readonly parameters
function processData(
  data: Readonly<UserData>,
  config: Readonly<ProcessConfig>,
): ProcessResult {
  // data and config cannot be mutated
  return {success: true};
}

// GOOD: const bindings
function calculateTotal(items: ReadonlyArray<Item>): number {
  const taxRate = 0.08;
  const subtotal = items.reduce((sum, item) => sum + item.price, 0);
  const tax = subtotal * taxRate;
  return subtotal + tax;
}

// BAD: mutable parameters
function processData(data: UserData, config: ProcessConfig): ProcessResult {
  data.processed = true; // mutation
  return {success: true};
}
引用类型参数标记为
Readonly<T>
。所有绑定默认使用
const
,除非需要修改。
typescript
// 规范:参数只读
function processData(
  data: Readonly<UserData>,
  config: Readonly<ProcessConfig>,
): ProcessResult {
  // data和config无法被修改
  return {success: true};
}

// 规范:绑定使用const
function calculateTotal(items: ReadonlyArray<Item>): number {
  const taxRate = 0.08;
  const subtotal = items.reduce((sum, item) => sum + item.price, 0);
  const tax = subtotal * taxRate;
  return subtotal + tax;
}

// 不规范:可变参数
function processData(data: UserData, config: ProcessConfig): ProcessResult {
  data.processed = true; // 修改参数
  return {success: true};
}

Arrays

数组

ALWAYS use
Array<T>
or
ReadonlyArray<T>
. NEVER use
T[]
syntax.
typescript
// GOOD: Array<T> syntax
const numbers: Array<number> = [1, 2, 3];
const roles: Array<UserRole> = ['admin', 'editor'];
function calculateAverage(numbers: ReadonlyArray<number>): number {
  return numbers.reduce((a, b) => a + b, 0) / numbers.length;
}

// BAD: T[] syntax (don't use this even if common in examples)
const numbers: number[] = [1, 2, 3];  // NO
const roles: UserRole[] = ['admin'];   // NO
function calculateAverage(numbers: number[]): number { // NO
  // ...
}
Why: Consistency with other generic syntax.
Array<T>
is explicit and matches
ReadonlyArray<T>
,
Record<K, V>
,
Promise<T>
, etc. The
T[]
syntax is muscle memory from other languages but inconsistent with TypeScript's generic patterns.
Prefer readonly outside local scope:
typescript
// GOOD: readonly array for function parameter
function calculateAverage(numbers: ReadonlyArray<number>): number {
  return numbers.reduce((a, b) => a + b, 0) / numbers.length;
}

// GOOD: mutable array in local scope
function processItems(items: ReadonlyArray<Item>): Array<ProcessedItem> {
  const results: Array<ProcessedItem> = [];
  for (const item of items) {
    results.push(transformItem(item));
  }
  return results;
}
始终使用
Array<T>
ReadonlyArray<T>
,绝不使用
T[]
语法。
typescript
// 规范:Array<T>语法
const numbers: Array<number> = [1, 2, 3];
const roles: Array<UserRole> = ['admin', 'editor'];
function calculateAverage(numbers: ReadonlyArray<number>): number {
  return numbers.reduce((a, b) => a + b, 0) / numbers.length;
}

// 不规范:T[]语法(即使示例中常见也不要使用)
const numbers: number[] = [1, 2, 3];  // 禁止
const roles: UserRole[] = ['admin'];   // 禁止
function calculateAverage(numbers: number[]): number { // 禁止
  // ...
}
理由: 与其他泛型语法保持一致。
Array<T>
更显式,且与
ReadonlyArray<T>
Record<K, V>
Promise<T>
等语法匹配。
T[]
是其他语言的肌肉记忆,但与TypeScript的泛型模式不一致。
局部作用域外优先使用只读:
typescript
// 规范:函数参数使用只读数组
function calculateAverage(numbers: ReadonlyArray<number>): number {
  return numbers.reduce((a, b) => a + b, 0) / numbers.length;
}

// 规范:局部作用域使用可变数组
function processItems(items: ReadonlyArray<Item>): Array<ProcessedItem> {
  const results: Array<ProcessedItem> = [];
  for (const item of items) {
    results.push(transformItem(item));
  }
  return results;
}

Deep Immutability

深度不可变性

Use
Readonly<T>
for shallow immutability,
ReadonlyDeep<T>
from type-fest when you need immutability all the way down.
typescript
import type {ReadonlyDeep} from 'type-fest';

// GOOD: shallow readonly for flat objects
type UserData = Readonly<{
  id: string;
  name: string;
  email: string;
}>;

// GOOD: deep readonly for nested structures
type AppConfig = ReadonlyDeep<{
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
  features: {
    enabled: Array<string>;
  };
}>;

function loadConfig(config: AppConfig): void {
  // config is deeply immutable
  // config.database.credentials.username = 'x'; // ERROR
}
浅不可变性使用
Readonly<T>
,需要深层不可变性时使用type-fest库的
ReadonlyDeep<T>
typescript
import type {ReadonlyDeep} from 'type-fest';

// 规范:扁平对象使用浅只读
type UserData = Readonly<{
  id: string;
  name: string;
  email: string;
}>;

// 规范:嵌套结构使用深度只读
type AppConfig = ReadonlyDeep<{
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
  features: {
    enabled: Array<string>;
  };
}>;

function loadConfig(config: AppConfig): void {
  // config是深度不可变的
  // config.database.credentials.username = 'x'; // 报错
}

Mathematics and Currency

数学与货币计算

When to Use math.js

何时使用math.js

ALWAYS use math.js for:
  • Currency calculations (money)
  • Financial calculations (interest, ROI, profit margins)
  • Precision-critical percentages
  • Complex mathematical operations requiring high precision
NEVER use JavaScript
number
for:
  • Money / currency amounts
  • Financial reporting calculations
  • Any calculation where precision errors are unacceptable
typescript
import { create, all, MathJsInstance } from 'mathjs';

const math: MathJsInstance = create(all);

// GOOD: math.js for currency calculations
function calculateTotal(
  price: number,
  quantity: number,
  taxRate: number
): string {
  const subtotal = math.multiply(
    math.bignumber(price),
    math.bignumber(quantity)
  );
  const tax = math.multiply(subtotal, math.bignumber(taxRate));
  const total = math.add(subtotal, tax);

  return math.format(total, { precision: 14 });
}

// GOOD: math.js for financial calculations
function calculateROI(
  initialInvestment: number,
  finalValue: number
): string {
  const initial = math.bignumber(initialInvestment);
  const final = math.bignumber(finalValue);
  const difference = math.subtract(final, initial);
  const ratio = math.divide(difference, initial);
  const percentage = math.multiply(ratio, 100);

  return math.format(percentage, { precision: 14 });
}

// BAD: JavaScript number for currency
function calculateTotal(price: number, quantity: number, taxRate: number): number {
  const subtotal = price * quantity;          // NO: precision errors
  const tax = subtotal * taxRate;             // NO: compounding errors
  return subtotal + tax;                      // NO: wrong for money
}

// BAD: JavaScript number for percentages in finance
function calculateDiscount(price: number, discountPercent: number): number {
  return price * (discountPercent / 100);     // NO: precision errors
}
Why math.js:
  • JavaScript's native
    number
    uses IEEE 754 double-precision floating-point
  • This causes precision errors:
    0.1 + 0.2 !== 0.3
  • For financial calculations, these errors are unacceptable
  • math.js BigNumber provides arbitrary precision arithmetic
When JavaScript number is OK:
  • Counters and indices
  • Simple integer math (within safe integer range)
  • Display coordinates, dimensions
  • Non-critical calculations where precision doesn't matter
以下场景必须使用math.js:
  • 货币计算(金额)
  • 金融计算(利息、投资回报率、利润率)
  • 精度要求高的百分比计算
  • 需要高精度的复杂数学运算
以下场景绝不能使用JavaScript原生
number
类型:
  • 金额/货币数值
  • 财务报表计算
  • 任何精度错误不可接受的计算
typescript
import { create, all, MathJsInstance } from 'mathjs';

const math: MathJsInstance = create(all);

// 规范:货币计算使用math.js
function calculateTotal(
  price: number,
  quantity: number,
  taxRate: number
): string {
  const subtotal = math.multiply(
    math.bignumber(price),
    math.bignumber(quantity)
  );
  const tax = math.multiply(subtotal, math.bignumber(taxRate));
  const total = math.add(subtotal, tax);

  return math.format(total, { precision: 14 });
}

// 规范:金融计算使用math.js
function calculateROI(
  initialInvestment: number,
  finalValue: number
): string {
  const initial = math.bignumber(initialInvestment);
  const final = math.bignumber(finalValue);
  const difference = math.subtract(final, initial);
  const ratio = math.divide(difference, initial);
  const percentage = math.multiply(ratio, 100);

  return math.format(percentage, { precision: 14 });
}

// 不规范:用JavaScript number处理货币
function calculateTotal(price: number, quantity: number, taxRate: number): number {
  const subtotal = price * quantity;          // 禁止:精度错误
  const tax = subtotal * taxRate;             // 禁止:误差累积
  return subtotal + tax;                      // 禁止:货币计算错误
}

// 不规范:用JavaScript number处理金融百分比
function calculateDiscount(price: number, discountPercent: number): number {
  return price * (discountPercent / 100);     // 禁止:精度错误
}
为什么用math.js:
  • JavaScript原生
    number
    使用IEEE 754双精度浮点数
  • 会导致精度错误:
    0.1 + 0.2 !== 0.3
  • 金融计算中,这些错误是不可接受的
  • math.js的BigNumber提供任意精度算术
JavaScript number可接受的场景:
  • 计数器与索引
  • 简单整数运算(在安全整数范围内)
  • 显示坐标、尺寸
  • 非关键计算,精度无关紧要

Nullability

空值处理

Null vs Undefined

Null vs Undefined

Use
null
for absent values.
undefined
means uninitialized. Proactively coalesce to null.
typescript
// GOOD: null for absent, undefined for uninitialized
type User = {
  name: string;
  email: string;
  phone: string | null; // may be absent
};

function findUser(id: string): User | null {
  const user = database.users.get(id);
  return user ?? null; // coalesce undefined to null
}

// GOOD: optional properties use ?:
type UserOptions = {
  name: string;
  email: string;
  newsletter?: boolean; // may be undefined
};

// BAD: undefined for absent values
function findUser(id: string): User | undefined {
  // prefer null for explicit absence
}

// GOOD: coalescing array access
const arr: Array<number> = [1, 2, 3];
const value: number | null = arr[10] ?? null;
缺失值使用
null
undefined
表示未初始化。主动将值合并为null。
typescript
// 规范:null表示缺失,undefined表示未初始化
type User = {
  name: string;
  email: string;
  phone: string | null; // 可能缺失
};

function findUser(id: string): User | null {
  const user = database.users.get(id);
  return user ?? null; // 将undefined合并为null
}

// 规范:可选属性使用?:
type UserOptions = {
  name: string;
  email: string;
  newsletter?: boolean; // 可能为undefined
};

// 不规范:用undefined表示缺失值
function findUser(id: string): User | undefined {
  // 优先用null表示明确的缺失
}

// 规范:数组访问时合并空值
const arr: Array<number> = [1, 2, 3];
const value: number | null = arr[10] ?? null;

Enums and Unions

枚举与联合类型

Prefer String Literal Unions

优先使用字符串字面量联合类型

Avoid enums. Use string literal unions instead.
typescript
// GOOD: string literal union
type Status = 'pending' | 'active' | 'complete' | 'failed';

function processStatus(status: Status): void {
  switch (status) {
    case 'pending':
      // handle pending
      break;
    case 'active':
      // handle active
      break;
    case 'complete':
      // handle complete
      break;
    case 'failed':
      // handle failed
      break;
  }
}

// BAD: enum
enum Status {
  Pending = 'pending',
  Active = 'active',
  Complete = 'complete',
  Failed = 'failed',
}
Rationale: String literal unions are simpler, work better with discriminated unions, and don't generate runtime code.
避免使用枚举,改用字符串字面量联合类型。
typescript
// 规范:字符串字面量联合类型
type Status = 'pending' | 'active' | 'complete' | 'failed';

function processStatus(status: Status): void {
  switch (status) {
    case 'pending':
      // 处理pending状态
      break;
    case 'active':
      // 处理active状态
      break;
    case 'complete':
      // 处理complete状态
      break;
    case 'failed':
      // 处理failed状态
      break;
  }
}

// 不规范:枚举
enum Status {
  Pending = 'pending',
  Active = 'active',
  Complete = 'complete',
  Failed = 'failed',
}
理由: 字符串字面量联合类型更简单,与可区分联合类型配合更好,且不会生成运行时代码。

Type Safety

类型安全

Never Use 'any'

绝不使用'any'

Always use
unknown
for truly unknown data. If a library forces
any
, escalate to operator for replacement.
typescript
// GOOD: unknown with type guard
function parseJSON(json: string): unknown {
  return JSON.parse(json);
}

function processData(json: string): User {
  const data: unknown = parseJSON(json);
  if (isUser(data)) {
    return data;
  }
  throw new Error('Invalid user data');
}

function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'name' in value &&
    'email' in value
  );
}

// BAD: using any
function parseJSON(json: string): any {
  return JSON.parse(json);
}
真正未知的数据始终使用
unknown
。如果库强制使用
any
,请向负责人申请替换该库。
typescript
// 规范:unknown配合类型守卫
function parseJSON(json: string): unknown {
  return JSON.parse(json);
}

function processData(json: string): User {
  const data: unknown = parseJSON(json);
  if (isUser(data)) {
    return data;
  }
  throw new Error('无效的用户数据');
}

function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'name' in value &&
    'email' in value
  );
}

// 不规范:使用any
function parseJSON(json: string): any {
  return JSON.parse(json);
}

Type Assertions

类型断言

Only for TypeScript system limitations. Always include comment explaining why.
typescript
// OK: DOM API limitation
const input = document.getElementById('email') as HTMLInputElement;
// DOM API returns HTMLElement, but we know it's an input

// OK: after runtime validation
const data: unknown = JSON.parse(jsonString);
if (isUser(data)) {
  const user = data; // type guard narrows to User
}

// BAD: assertion without validation
const user = data as User; // no runtime check

// BAD: assertion to avoid type error
const value = (someValue as any) as TargetType;
仅在TypeScript系统限制下使用,始终添加注释说明原因。
typescript
// 可接受:DOM API限制
const input = document.getElementById('email') as HTMLInputElement;
// DOM API返回HTMLElement,但我们知道它是input元素

// 可接受:运行时验证后
const data: unknown = JSON.parse(jsonString);
if (isUser(data)) {
  const user = data; // 类型守卫将类型收窄为User
}

// 不规范:无验证的断言
const user = data as User; // 无运行时检查

// 不规范:为避免类型错误而断言
const value = (someValue as any) as TargetType;

Non-null Assertion (!)

非空断言(!)

Same rules as type assertions - sparingly, with justification.
typescript
// OK: after explicit check
const user = users.find(u => u.id === targetId);
if (user) {
  processUser(user); // user is non-null here, no need for !
}

// OK (with comment): known initialization pattern
class Service {
  private connection!: Connection;
  // connection initialized in async init() called by constructor

  constructor() {
    this.init();
  }

  private async init(): Promise<void> {
    this.connection = await createConnection();
  }
}

// BAD: hiding real potential null
const value = map.get(key)!; // what if key doesn't exist?
与类型断言规则相同——谨慎使用,且需说明理由。
typescript
// 可接受:显式检查后
const user = users.find(u => u.id === targetId);
if (user) {
  processUser(user); // 此处user非空,无需使用!
}

// 可接受(带注释):已知初始化模式
class Service {
  private connection!: Connection;
  // connection在构造函数调用的async init()中初始化

  constructor() {
    this.init();
  }

  private async init(): Promise<void> {
    this.connection = await createConnection();
  }
}

// 不规范:隐藏潜在的空值
const value = map.get(key)!; // 如果key不存在怎么办?

Type Guards

类型守卫

Use type guards to narrow unknown types. Prefer built-in checks when possible.
typescript
// GOOD: typeof/instanceof for primitives/classes
function processValue(value: unknown): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  if (typeof value === 'number') {
    return value.toString();
  }
  throw new Error('Unsupported type');
}

// GOOD: custom type guard with 'is'
function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'name' in value &&
    typeof (value as any).name === 'string' &&
    'email' in value &&
    typeof (value as any).email === 'string'
  );
}

// GOOD: discriminated union
type Result =
  | {type: 'success'; data: string}
  | {type: 'error'; message: string};

function handleResult(result: Result): void {
  if (result.type === 'success') {
    console.log(result.data); // narrowed to success
  } else {
    console.error(result.message); // narrowed to error
  }
}

// GOOD: schema validation (TypeBox preferred)
import {Type, Static} from '@sinclair/typebox';

const UserSchema = Type.Object({
  name: Type.String(),
  email: Type.String(),
  age: Type.Number(),
});

type User = Static<typeof UserSchema>;

function validateUser(data: unknown): data is User {
  return Value.Check(UserSchema, data);
}
使用类型守卫收窄unknown类型,优先使用内置检查。
typescript
// 规范:对原始类型/类使用typeof/instanceof
function processValue(value: unknown): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  if (typeof value === 'number') {
    return value.toString();
  }
  throw new Error('不支持的类型');
}

// 规范:自定义带'is'的类型守卫
function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'name' in value &&
    typeof (value as any).name === 'string' &&
    'email' in value &&
    typeof (value as any).email === 'string'
  );
}

// 规范:可区分联合类型
type Result =
  | {type: 'success'; data: string}
  | {type: 'error'; message: string};

function handleResult(result: Result): void {
  if (result.type === 'success') {
    console.log(result.data); // 收窄为success类型
  } else {
    console.error(result.message); // 收窄为error类型
  }
}

// 规范:Schema验证(优先使用TypeBox)
import {Type, Static} from '@sinclair/typebox';

const UserSchema = Type.Object({
  name: Type.String(),
  email: Type.String(),
  age: Type.Number(),
});

type User = Static<typeof UserSchema>;

function validateUser(data: unknown): data is User {
  return Value.Check(UserSchema, data);
}

Generics

泛型

Generic Constraints

泛型约束

Always constrain generics when possible. Use descriptive names.
typescript
// GOOD: constrained with descriptive name
function mapItems<TItem, TResult>(
  items: ReadonlyArray<TItem>,
  mapper: (item: TItem) => TResult,
): Array<TResult> {
  return items.map(mapper);
}

// GOOD: constraint on generic
function getProperty<TObj extends object, TKey extends keyof TObj>(
  obj: TObj,
  key: TKey,
): TObj[TKey] {
  return obj[key];
}

// BAD: unconstrained, single-letter names
function getProperty<T, K>(obj: T, key: K): any {
  return (obj as any)[key];
}
尽可能约束泛型,使用描述性名称。
typescript
// 规范:带描述性名称的约束泛型
function mapItems<TItem, TResult>(
  items: ReadonlyArray<TItem>,
  mapper: (item: TItem) => TResult,
): Array<TResult> {
  return items.map(mapper);
}

// 规范:泛型约束
function getProperty<TObj extends object, TKey extends keyof TObj>(
  obj: TObj,
  key: TKey,
): TObj[TKey] {
  return obj[key];
}

// 不规范:无约束、单字母名称
function getProperty<T, K>(obj: T, key: K): any {
  return (obj as any)[key];
}

Avoid Over-Generalization

避免过度泛化

Don't make things generic unless multiple concrete types will use it.
typescript
// GOOD: specific types for single use case
function formatUser(user: User): string {
  return `${user.name} <${user.email}>`;
}

// BAD: unnecessary generic
function format<T extends {name: string; email: string}>(item: T): string {
  return `${item.name} <${item.email}>`;
}
除非有多个具体类型会使用,否则不要将代码泛型化。
typescript
// 规范:单一场景使用具体类型
function formatUser(user: User): string {
  return `${user.name} <${user.email}>`;
}

// 不规范:不必要的泛型
function format<T extends {name: string; email: string}>(item: T): string {
  return `${item.name} <${item.email}>`;
}

Utility Types

工具类型

Built-in vs type-fest

内置工具类型vs type-fest

Use built-in utilities when available. Use type-fest for deep operations and specialized needs.
typescript
// GOOD: built-in utilities
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type UserKeys = keyof User;
type UserValues = User[keyof User];

// GOOD: type-fest for deep operations
import type {PartialDeep, RequiredDeep, ReadonlyDeep} from 'type-fest';

type DeepPartialConfig = PartialDeep<AppConfig>;
type DeepRequiredConfig = RequiredDeep<AppConfig>;
优先使用内置工具类型,深度操作和特殊需求使用type-fest。
typescript
// 规范:使用内置工具类型
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type UserKeys = keyof User;
type UserValues = User[keyof User];

// 规范:深度操作使用type-fest
import type {PartialDeep, RequiredDeep, ReadonlyDeep} from 'type-fest';

type DeepPartialConfig = PartialDeep<AppConfig>;
type DeepRequiredConfig = RequiredDeep<AppConfig>;

Object Property Access

对象属性访问

Use
Record<K, V>
for objects with dynamic keys.
typescript
// GOOD: Record for dynamic keys
type UserCache = Record<string, User>;

function getUser(cache: UserCache, id: string): User | null {
  return cache[id] ?? null;
}

// BAD: index signature
type UserCache = {
  [key: string]: User;
};
动态键的对象使用
Record<K, V>
typescript
// 规范:动态键使用Record
type UserCache = Record<string, User>;

function getUser(cache: UserCache, id: string): User | null {
  return cache[id] ?? null;
}

// 不规范:索引签名
type UserCache = {
  [key: string]: User;
};

Derived Types

派生类型

Use mapped types for transformations. Create explicit types for complex derivations.
typescript
// GOOD: mapped type for simple transformation
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

type NullableUser = Nullable<User>;

// GOOD: explicit type for complex case
type UserUpdateData = {
  name?: string;
  email?: string;
  // exclude id and other immutable fields explicitly
};

// BAD: overly clever utility type usage
type UserUpdateData = Omit<Partial<User>, 'id' | 'createdAt' | 'updatedAt'>;
使用映射类型进行转换,复杂派生创建显式类型。
typescript
// 规范:简单转换使用映射类型
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

type NullableUser = Nullable<User>;

// 规范:复杂场景使用显式类型
type UserUpdateData = {
  name?: string;
  email?: string;
  // 显式排除id和其他不可变字段
};

// 不规范:过度使用工具类型
type UserUpdateData = Omit<Partial<User>, 'id' | 'createdAt' | 'updatedAt'>;

Module Organization

模块组织

Exports

导出

Use named exports only. No default exports.
typescript
// GOOD: named exports
export function processUser(user: User): ProcessedUser {
  // implementation
}

export type ProcessedUser = {
  id: string;
  name: string;
};

// BAD: default export
export default function processUser(user: User): ProcessedUser {
  // implementation
}
仅使用具名导出,禁止默认导出。
typescript
// 规范:具名导出
export function processUser(user: User): ProcessedUser {
  // 实现代码
}

export type ProcessedUser = {
  id: string;
  name: string;
};

// 不规范:默认导出
export default function processUser(user: User): ProcessedUser {
  // 实现代码
}

Barrel Exports

桶导出

Use index.ts to re-export from directories.
typescript
// src/users/index.ts
export * from './user-service';
export * from './user-repository';
export * from './types';

// consumers can import from directory
import {UserService, type User} from './users';
使用index.ts重新导出目录内的模块。
typescript
// src/users/index.ts
export * from './user-service';
export * from './user-repository';
export * from './types';

// 消费者可从目录导入
import {UserService, type User} from './users';

Import Organization

导入组织

Group by source type, alphabetize within groups. Use destructuring for fewer than 3 imports.
typescript
// GOOD: organized imports
// External dependencies
import {Result, ok, err} from 'neverthrow';
import type {ReadonlyDeep} from 'type-fest';

// Internal modules
import {DatabaseService} from '@/services/database';
import {Logger} from '@/services/logger';

// Relative imports
import {UserRepository} from './user-repository';
import type {User, UserData} from './types';

// GOOD: destructure for < 3 imports
import {foo, bar} from './utils';

// GOOD: namespace for 3+ imports
import * as utils from './utils';
utils.foo();
utils.bar();
utils.baz();
Note: eslint-import plugin should be configured to enforce import ordering.
按来源类型分组,组内按字母排序。少于3个导入时使用解构。
typescript
// 规范:组织有序的导入
// 外部依赖
import {Result, ok, err} from 'neverthrow';
import type {ReadonlyDeep} from 'type-fest';

// 内部模块
import {DatabaseService} from '@/services/database';
import {Logger} from '@/services/logger';

// 相对导入
import {UserRepository} from './user-repository';
import type {User, UserData} from './types';

// 规范:<3个导入时使用解构
import {foo, bar} from './utils';

// 规范:≥3个导入时使用命名空间
import * as utils from './utils';
utils.foo();
utils.bar();
utils.baz();
注意: 应配置eslint-import插件以强制导入顺序。

FCIS Integration

FCIS集成

Functional Core Patterns

函数式核心模式

Return Result types. Never throw exceptions. Pure functions only.
typescript
// pattern: Functional Core
import {Result, ok, err} from 'neverthrow';

type ValidationError = {
  field: string;
  message: string;
};

// GOOD: returns Result, pure function
function validateUser(
  data: Readonly<UserData>,
): Result<ValidUser, ValidationError> {
  if (!data.email) {
    return err({field: 'email', message: 'Email required'});
  }
  if (!data.name) {
    return err({field: 'name', message: 'Name required'});
  }
  return ok({...data, validated: true});
}

// GOOD: transformation with Result
function transformUser(
  user: Readonly<User>,
  config: Readonly<TransformConfig>,
): Result<TransformedUser, TransformError> {
  // pure transformation logic
  return ok(transformed);
}
返回Result类型,绝不抛出异常,仅包含纯函数。
typescript
// 模式:函数式核心
import {Result, ok, err} from 'neverthrow';

type ValidationError = {
  field: string;
  message: string;
};

// 规范:返回Result,纯函数
function validateUser(
  data: Readonly<UserData>,
): Result<ValidUser, ValidationError> {
  if (!data.email) {
    return err({field: 'email', message: '邮箱为必填项'});
  }
  if (!data.name) {
    return err({field: 'name', message: '姓名为必填项'});
  }
  return ok({...data, validated: true});
}

// 规范:带Result的转换函数
function transformUser(
  user: Readonly<User>,
  config: Readonly<TransformConfig>,
): Result<TransformedUser, TransformError> {
  // 纯转换逻辑
  return ok(transformed);
}

Imperative Shell Patterns

命令式外壳模式

May throw exceptions. Orchestrate I/O. Minimal business logic.
typescript
// pattern: Imperative Shell
import {HttpException} from './exceptions';

class UserController {
  constructor(
    private readonly userRepo: UserRepository,
    private readonly logger: Logger,
  ) {}

  // GOOD: orchestrates I/O, delegates to Core, may throw
  async createUser(data: UserData): Promise<User> {
    this.logger.info('Creating user', {email: data.email});

    // Delegate validation to Functional Core
    const validationResult = validateUser(data);
    if (validationResult.isErr()) {
      throw new HttpException(400, validationResult.error.message);
    }

    // I/O operation
    const user = await this.userRepo.create(validationResult.value);

    this.logger.info('User created', {id: user.id});
    return user;
  }
}
可抛出异常,编排I/O操作,包含最少业务逻辑。
typescript
// 模式:命令式外壳
import {HttpException} from './exceptions';

class UserController {
  constructor(
    private readonly userRepo: UserRepository,
    private readonly logger: Logger,
  ) {}

  // 规范:编排I/O,委托给核心,可抛出异常
  async createUser(data: UserData): Promise<User> {
    this.logger.info('创建用户', {email: data.email});

    // 委托验证给函数式核心
    const validationResult = validateUser(data);
    if (validationResult.isErr()) {
      throw new HttpException(400, validationResult.error.message);
    }

    // I/O操作
    const user = await this.userRepo.create(validationResult.value);

    this.logger.info('用户创建成功', {id: user.id});
    return user;
  }
}

Compiler Configuration

编译器配置

Strictness

严格模式

Full strict mode plus additional checks.
json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}
All strict options are mandatory. No exceptions.
启用完整严格模式及额外检查。
json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}
所有严格选项都是强制的,无例外。

Testing

测试

Test Type Safety

测试中的类型安全

Allow type assertions in tests for test data setup.
typescript
// OK in tests: type assertions for test data
const mockUser = {
  id: '123',
  name: 'Test User',
} as User;

// GOOD: factory functions
function createTestUser(overrides?: Partial<User>): User {
  return {
    id: '123',
    name: 'Test User',
    email: 'test@example.com',
    ...overrides,
  };
}
测试数据设置中允许使用类型断言。
typescript
// 测试中可接受:测试数据的类型断言
const mockUser = {
  id: '123',
  name: '测试用户',
} as User;

// 规范:使用工厂函数
function createTestUser(overrides?: Partial<User>): User {
  return {
    id: '123',
    name: '测试用户',
    email: 'test@example.com',
    ...overrides,
  };
}

Tools and Libraries

工具与库

Standard Stack

标准技术栈

  • Type utilities: type-fest for deep operations and specialized utilities
  • Validation: TypeBox preferred over zod (avoid decorator-based libraries)
  • Result types: neverthrow for functional error handling
  • Linting: eslint-import for import ordering
  • 类型工具: type-fest 用于深度操作和特殊工具类型
  • 验证: 优先使用TypeBox而非zod(避免基于装饰器的库)
  • Result类型: neverthrow用于函数式错误处理
  • 代码检查: eslint-import用于导入排序

Library Selection

库选择

When choosing between libraries, ALWAYS prefer the one without decorators.
typescript
// AVOID: decorator-based libraries
import {IsEmail, IsString} from 'class-validator';

class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

// PREFER: schema-based validation
import {Type} from '@sinclair/typebox';

const CreateUserSchema = Type.Object({
  name: Type.String(),
  email: Type.String({format: 'email'}),
});
选择库时,始终优先选择无装饰器的库。
typescript
// 避免:基于装饰器的库
import {IsEmail, IsString} from 'class-validator';

class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

// 优先:基于Schema的验证
import {Type} from '@sinclair/typebox';

const CreateUserSchema = Type.Object({
  name: Type.String(),
  email: Type.String({format: 'email'}),
});

Documentation

文档

JSDoc for Public APIs

公共API的JSDoc

Use JSDoc comments for exported functions and types.
typescript
/**
 * Processes user data and returns a validated user object.
 *
 * @param data - Raw user data to process
 * @returns Result containing validated user or validation error
 */
export function validateUser(
  data: Readonly<UserData>,
): Result<ValidUser, ValidationError> {
  // implementation
}

/**
 * Configuration options for user processing.
 */
export type ProcessUserOptions = {
  /** User's full name */
  readonly name: string;
  /** User's email address */
  readonly email: string;
  /** Whether to send welcome email (default: true) */
  readonly sendWelcome?: boolean;
};
导出的函数和类型使用JSDoc注释。
typescript
/**
 * 处理用户数据并返回验证后的用户对象。
 *
 * @param data - 待处理的原始用户数据
 * @returns 包含验证后用户或验证错误的Result
 */
export function validateUser(
  data: Readonly<UserData>,
): Result<ValidUser, ValidationError> {
  // 实现代码
}

/**
 * 用户处理的配置选项。
 */
export type ProcessUserOptions = {
  /** 用户全名 */
  readonly name: string;
  /** 用户邮箱地址 */
  readonly email: string;
  /** 是否发送欢迎邮件(默认:true) */
  readonly sendWelcome?: boolean;
};

Abstraction Guidelines

抽象指南

When to Abstract

何时抽象

Follow rule of three. Abstract when types become complex (3+ properties/levels).
typescript
// GOOD: abstract after third repetition
// First use
const user1 = {id: '1', name: 'Alice', email: 'alice@example.com'};

// Second use
const user2 = {id: '2', name: 'Bob', email: 'bob@example.com'};

// Third use - now abstract
type User = {
  id: string;
  name: string;
  email: string;
};

// GOOD: abstract complex inline types
// Before
function process(data: {
  user: {name: string; email: string};
  settings: {theme: string; notifications: boolean};
}): void {}

// After - extract when > 3 properties or nested
type UserInfo = {
  name: string;
  email: string;
};

type UserSettings = {
  theme: string;
  notifications: boolean;
};

type ProcessData = {
  user: UserInfo;
  settings: UserSettings;
};

function process(data: Readonly<ProcessData>): void {}
遵循三次原则。当类型变得复杂(≥3个属性/层级)时进行抽象。
typescript
// 规范:第三次重复时抽象
// 第一次使用
const user1 = {id: '1', name: 'Alice', email: 'alice@example.com'};

// 第二次使用
const user2 = {id: '2', name: 'Bob', email: 'bob@example.com'};

// 第三次使用 - 现在抽象
type User = {
  id: string;
  name: string;
  email: string;
};

// 规范:复杂内联类型抽象
// 抽象前
function process(data: {
  user: {name: string; email: string};
  settings: {theme: string; notifications: boolean};
}): void {}

// 抽象后 - 属性>3个或嵌套时提取
type UserInfo = {
  name: string;
  email: string;
};

type UserSettings = {
  theme: string;
  notifications: boolean;
};

type ProcessData = {
  user: UserInfo;
  settings: UserSettings;
};

function process(data: Readonly<ProcessData>): void {}

Sharp Edges

注意事项

Runtime hazards that TypeScript doesn't catch. Know these cold.
TypeScript无法捕获的运行时风险,必须牢记。

Equality

相等性

Always use
===
. Never use
==
.
typescript
// BAD: loose equality has surprising coercion
"0" == false;   // true
[] == ![];      // true
null == undefined; // true

// GOOD: strict equality
"0" === false;  // false
[] === ![];     // false
null === undefined; // false
TypeScript won't save you here—both are valid syntax.
始终使用
===
,绝不使用
==
typescript
// 不规范:松散相等会导致意外的类型转换
"0" == false;   // true
[] == ![];      // true
null == undefined; // true

// 规范:严格相等
"0" === false;  // false
[] === ![];     // false
null === undefined; // false
TypeScript无法帮你避免这个问题——两种语法都是合法的。

Prototype Pollution

原型污染

Never merge untrusted objects into plain objects.
typescript
// DANGEROUS: merging user input
const userInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, userInput); // pollutes Object.prototype

// SAFE: use Map for dynamic keys from untrusted sources
const safeStore = new Map<string, unknown>();
safeStore.set(key, value);

// SAFE: null-prototype object
const safeObj = Object.create(null) as Record<string, unknown>;

// SAFE: validate keys before merge
function safeMerge<T extends object>(target: T, source: unknown): T {
  if (typeof source !== 'object' || source === null) return target;
  for (const key of Object.keys(source)) {
    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
      continue; // skip dangerous keys
    }
    (target as Record<string, unknown>)[key] = (source as Record<string, unknown>)[key];
  }
  return target;
}
绝不要将不可信对象合并到普通对象中。
typescript
// 危险:合并用户输入
const userInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, userInput); // 污染Object.prototype

// 安全:用Map存储不可信来源的动态键
const safeStore = new Map<string, unknown>();
safeStore.set(key, value);

// 安全:无原型对象
const safeObj = Object.create(null) as Record<string, unknown>;

// 安全:合并前验证键
function safeMerge<T extends object>(target: T, source: unknown): T {
  if (typeof source !== 'object' || source === null) return target;
  for (const key of Object.keys(source)) {
    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
      continue; // 跳过危险键
    }
    (target as Record<string, unknown>)[key] = (source as Record<string, unknown>)[key];
  }
  return target;
}

Regular Expression DoS (ReDoS)

正则表达式拒绝服务(ReDoS)

Avoid nested quantifiers and overlapping alternatives.
typescript
// DANGEROUS: catastrophic backtracking
const bad1 = /(a+)+$/;           // nested quantifiers
const bad2 = /(a|a)+$/;          // overlapping alternatives
const bad3 = /(\w+)*$/;          // greedy quantifier in group with quantifier

// These can freeze the event loop on crafted input like "aaaaaaaaaaaaaaaaaaaaaaaa!"

// SAFER: avoid nesting, use possessive-like patterns
const safer = /a+$/;             // no nesting
const safest = /^[a-z]+$/;       // anchored, simple character class
When accepting user-provided regex patterns, use a timeout or run in a worker.
避免嵌套量词和重叠的备选分支。
typescript
// 危险:灾难性回溯
const bad1 = /(a+)+$/;           // 嵌套量词
const bad2 = /(a|a)+$/;          // 重叠备选分支
const bad3 = /(\w+)*$/;          // 组内的贪婪量词

// 这些正则表达式在处理精心构造的输入(如"aaaaaaaaaaaaaaaaaaaaaaaa!")时会阻塞事件循环

// 更安全:避免嵌套,使用类似独占量词的模式
const safer = /a+$/;             // 无嵌套
const safest = /^[a-z]+$/;       // 锚定,简单字符类
当接受用户提供的正则表达式时,使用超时机制或在worker中运行。

parseInt Radix

parseInt基数

Always specify the radix parameter.
typescript
// BAD: radix varies by engine/input
parseInt("08");     // 0 or 8 depending on engine
parseInt("0x10");   // 16 (hex prefix always recognized)

// GOOD: explicit radix
parseInt("08", 10);   // 8
parseInt("10", 16);   // 16
parseInt("1010", 2);  // 10

// BETTER: use Number() for decimal
Number("08");         // 8
Number.parseInt("08", 10); // 8
始终指定基数参数。
typescript
// 不规范:基数因引擎/输入而异
parseInt("08");     // 0或8,取决于引擎
parseInt("0x10");   // 16(始终识别十六进制前缀)

// 规范:显式指定基数
parseInt("08", 10);   // 8
parseInt("10", 16);   // 16
parseInt("1010", 2);  // 10

// 更好:十进制转换用Number()
Number("08");         // 8
Number.parseInt("08", 10); // 8

Array Mutations

数组修改

Know which methods mutate in place.
MutatesReturns new array
.sort()
.toSorted()
(ES2023)
.reverse()
.toReversed()
(ES2023)
.splice()
.toSpliced()
(ES2023)
.push()
,
.pop()
.concat()
,
.slice()
.shift()
,
.unshift()
spread:
[first, ...rest]
.fill()
-
typescript
// BAD: mutates original
const original = [3, 1, 2];
const sorted = original.sort(); // original is now [1, 2, 3]

// GOOD: copy first (pre-ES2023)
const sorted = [...original].sort();
const sorted = original.slice().sort();

// GOOD: use non-mutating methods (ES2023+)
const sorted = original.toSorted();
const reversed = original.toReversed();
了解哪些方法会原地修改数组。
原地修改返回新数组
.sort()
.toSorted()
(ES2023)
.reverse()
.toReversed()
(ES2023)
.splice()
.toSpliced()
(ES2023)
.push()
,
.pop()
.concat()
,
.slice()
.shift()
,
.unshift()
展开语法:
[first, ...rest]
.fill()
-
typescript
// 不规范:修改原数组
const original = [3, 1, 2];
const sorted = original.sort(); // 原数组变为[1, 2, 3]

// 规范:先复制(ES2023之前)
const sorted = [...original].sort();
const sorted = original.slice().sort();

// 规范:使用非修改方法(ES2023+)
const sorted = original.toSorted();
const reversed = original.toReversed();

Numeric Sort

数值排序

Default sort is lexicographic, not numeric.
typescript
// WRONG: sorts as strings
[10, 2, 1].sort();  // [1, 10, 2]

// CORRECT: numeric comparator
[10, 2, 1].sort((a, b) => a - b);  // [1, 2, 10]

// Descending
[10, 2, 1].sort((a, b) => b - a);  // [10, 2, 1]
默认排序是字典序,而非数值序。
typescript
// 错误:按字符串排序
[10, 2, 1].sort();  // [1, 10, 2]

// 正确:数值比较器
[10, 2, 1].sort((a, b) => a - b);  // [1, 2, 10]

// 降序
[10, 2, 1].sort((a, b) => b - a);  // [10, 2, 1]

eval and Function Constructor

eval与Function构造函数

Never use eval() or new Function() with untrusted input.
typescript
// DANGEROUS: code injection
eval(userInput);                    // arbitrary code execution
new Function('return ' + userInput)(); // same risk

// If you need dynamic evaluation, use a sandboxed environment or parser
绝不要使用eval()或new Function()处理不可信输入。
typescript
// 危险:代码注入
eval(userInput);                    // 任意代码执行
new Function('return ' + userInput)(); // 同样风险

// 如果需要动态求值,使用沙箱环境或解析器

JSON Precision Loss

JSON精度丢失

JSON.parse loses precision for large integers and BigInt.
typescript
// PROBLEM: JavaScript numbers lose precision > 2^53
JSON.parse('{"id": 9007199254740993}'); // id becomes 9007199254740992

// PROBLEM: BigInt not supported
JSON.parse('{"value": 123n}'); // SyntaxError

// SOLUTION: use string representation for large IDs
type ApiResponse = {
  id: string; // "9007199254740993" - keep as string
};

// SOLUTION: use a BigInt-aware parser for financial data
// Or use string fields and parse with BigInt() after
JSON.parse会丢失大整数和BigInt的精度。
typescript
// 问题:JavaScript数字在>2^53时丢失精度
JSON.parse('{"id": 9007199254740993}'); // id变为9007199254740992

// 问题:不支持BigInt
JSON.parse('{"value": 123n}'); // 语法错误

// 解决方案:大ID使用字符串表示
type ApiResponse = {
  id: string; // "9007199254740993" - 保持为字符串
};

// 解决方案:金融数据使用支持BigInt的解析器
// 或使用字符串字段,之后用BigInt()解析

Promise.all vs Promise.allSettled

Promise.all vs Promise.allSettled

Promise.all fails fast; Promise.allSettled waits for all.
typescript
// Promise.all: rejects immediately on first failure
// Use when: all must succeed, fail fast is desired
async function fetchAllRequired(ids: ReadonlyArray<string>): Promise<Array<User>> {
  const promises = ids.map(id => fetchUser(id));
  return Promise.all(promises); // throws on first failure
}

// Promise.allSettled: waits for all, never rejects
// Use when: need results from successful ones even if some fail
async function fetchAllBestEffort(
  ids: ReadonlyArray<string>,
): Promise<Array<User>> {
  const promises = ids.map(id => fetchUser(id));
  const results = await Promise.allSettled(promises);

  return results
    .filter((r): r is PromiseFulfilledResult<User> => r.status === 'fulfilled')
    .map(r => r.value);
}

// Common patterns with allSettled
const results = await Promise.allSettled(promises);

const succeeded = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');

// Log failures, return successes
for (const failure of failed) {
  if (failure.status === 'rejected') {
    logger.error('Operation failed', {reason: failure.reason});
  }
}
MethodBehaviorUse when
Promise.all
Rejects on first failureAll must succeed
Promise.allSettled
Always resolves with status arrayNeed partial results
Promise.race
Resolves/rejects with first to completeTimeout patterns
Promise.any
Resolves with first success, rejects if all failFirst success wins
Promise.all快速失败;Promise.allSettled等待所有操作完成。
typescript
// Promise.all:第一个失败时立即拒绝
// 使用场景:所有操作必须成功,需要快速失败
async function fetchAllRequired(ids: ReadonlyArray<string>): Promise<Array<User>> {
  const promises = ids.map(id => fetchUser(id));
  return Promise.all(promises); // 第一个失败时抛出
}

// Promise.allSettled:等待所有操作完成,从不拒绝
// 使用场景:即使部分失败,也需要成功的结果
async function fetchAllBestEffort(
  ids: ReadonlyArray<string>,
): Promise<Array<User>> {
  const promises = ids.map(id => fetchUser(id));
  const results = await Promise.allSettled(promises);

  return results
    .filter((r): r is PromiseFulfilledResult<User> => r.status === 'fulfilled')
    .map(r => r.value);
}

// allSettled常见模式
const results = await Promise.allSettled(promises);

const succeeded = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');

// 记录失败,返回成功结果
for (const failure of failed) {
  if (failure.status === 'rejected') {
    logger.error('操作失败', {reason: failure.reason});
  }
}
方法行为使用场景
Promise.all
第一个失败时拒绝所有操作必须成功
Promise.allSettled
始终解析为状态数组需要部分结果
Promise.race
第一个完成时解析/拒绝超时模式
Promise.any
第一个成功时解析,全部失败时拒绝第一个成功即可

Unsafe Property Access

不安全的属性访问

Bracket notation with user input is dangerous.
typescript
// DANGEROUS: arbitrary property access
function getValue(obj: object, key: string): unknown {
  return (obj as Record<string, unknown>)[key]; // could access __proto__, constructor
}

// SAFER: validate or use Map
function safeGetValue(obj: Record<string, unknown>, key: string): unknown {
  if (!Object.hasOwn(obj, key)) return undefined;
  if (key === '__proto__' || key === 'constructor') return undefined;
  return obj[key];
}
使用括号语法访问用户输入的键是危险的。
typescript
// 危险:任意属性访问
function getValue(obj: object, key: string): unknown {
  return (obj as Record<string, unknown>)[key]; // 可能访问__proto__、constructor
}

// 更安全:验证或使用Map
function safeGetValue(obj: Record<string, unknown>, key: string): unknown {
  if (!Object.hasOwn(obj, key)) return undefined;
  if (key === '__proto__' || key === 'constructor') return undefined;
  return obj[key];
}

Common Mistakes

常见错误

MistakeFix
Using
interface
for data shapes
Use
type
instead
Using
any
in business logic
Use
unknown
+ type guards
const foo = () => {}
top-level declarations
Use
function foo() {}
Type assertions without validationAdd runtime validation or type guard
Mutable parametersMark as
Readonly<T>
for reference types
undefined
for absent values
Use
null
; coalesce with
?? null
EnumsUse string literal unions
Missing return types on exportsAlways type function returns
Using
T[]
for arrays
Use
Array<T>
or
ReadonlyArray<T>
JavaScript
number
for money/currency
Use math.js with BigNumber
Decorators (unless framework requires)Use functions or type-based solutions
Default exportsUse named exports only
Over-abstraction before third useWait for pattern to emerge
Title Case error messagesUse lowercase fragments:
failed to connect: timeout
Unnecessary async on pure functionsKeep sync unless I/O is involved
==
for comparisons
Use
===
always
parseInt()
without radix
Use
parseInt(str, 10)
or
Number()
.sort()
on numeric arrays without comparator
Use
.sort((a, b) => a - b)
Object.assign()
with untrusted input
Validate keys or use
Map
Nested regex quantifiers
(a+)+
Refactor to avoid ReDoS
Promise.all
when partial results acceptable
Use
Promise.allSettled
错误修复方案
interface
定义数据结构
改用
type
业务逻辑中使用
any
改用
unknown
+ 类型守卫
顶层函数用
const foo = () => {}
声明
改用
function foo() {}
无验证的类型断言添加运行时验证或类型守卫
可变参数引用类型参数标记为
Readonly<T>
undefined
表示缺失值
改用
null
;用
?? null
合并
使用枚举改用字符串字面量联合类型
导出函数缺失返回类型始终声明函数返回类型
T[]
表示数组
改用
Array<T>
ReadonlyArray<T>
用JavaScript
number
处理货币/金融计算
用math.js的BigNumber
使用装饰器(除非框架要求)改用函数或基于类型的方案
使用默认导出仅使用具名导出
第三次使用前就抽象等待模式出现后再抽象
错误消息使用标题大小写使用小写片段:
failed to connect: timeout
纯函数不必要地使用异步保持同步,除非涉及I/O
==
比较
始终用
===
parseInt()
未指定基数
parseInt(str, 10)
Number()
数值数组用
.sort()
但未传比较器
.sort((a, b) => a - b)
Object.assign()
处理不可信输入
验证键或使用
Map
正则表达式使用嵌套量词
(a+)+
重构以避免ReDoS
可接受部分结果时用
Promise.all
改用
Promise.allSettled

Red Flags

危险信号

STOP and refactor when you see:
  • any
    keyword in business logic
  • interface
    for data shapes (not class contracts)
  • JavaScript
    number
    for money, currency, or financial calculations
  • T[]
    instead of
    Array<T>
    syntax
  • Decorators in library selection
  • Type assertions without explanatory comments
  • Missing return types on exported functions
  • Mutable class fields (should be
    readonly
    )
  • undefined
    used for explicitly absent values
  • Enums instead of string literal unions
  • Default exports
  • Functions with 4+ positional parameters
  • Complex inline types used repeatedly
  • Async functions that don't perform I/O
  • Error messages in Title Case
  • ==
    instead of
    ===
  • eval()
    or
    new Function()
    with any dynamic input
  • Regex patterns with nested quantifiers
    (x+)+
    or
    (x|x)+
  • Object.assign()
    or spread with user-controlled objects
  • parseInt()
    without explicit radix
  • .sort()
    on numbers without comparator function
  • JSON.parse()
    on data with large integer IDs (use string IDs)
看到以下内容时,立即停止并重构:
  • 业务逻辑中出现
    any
    关键字
  • interface
    定义数据结构(而非类契约)
  • 用JavaScript
    number
    处理货币、金融计算
  • T[]
    而非
    Array<T>
    语法
  • 选择带装饰器的库
  • 无注释说明的类型断言
  • 导出函数缺失返回类型
  • 可变类字段(应设为
    readonly
  • undefined
    表示明确的缺失值
  • 用枚举而非字符串字面量联合类型
  • 使用默认导出
  • 函数有4个以上位置参数
  • 复杂内联类型重复使用
  • 未执行I/O的异步函数
  • 标题大小写的错误消息
  • ==
    而非
    ===
  • eval()
    new Function()
    处理动态输入
  • 正则表达式有嵌套量词
    (x+)+
    (x|x)+
  • Object.assign()
    或展开语法处理用户控制的对象
  • parseInt()
    未指定基数
  • 数值数组用
    .sort()
    但未传比较器
  • JSON.parse()
    处理含大整数ID的数据(改用字符串ID)

Reference

参考

For comprehensive type-fest utilities documentation, see type-fest.md.
For comprehensive TypeBox validator documentation, see typebox.md. Please note that we generally use AJV as the canonical validator, but TypeBox is the schema generator.
完整的type-fest工具类型文档,请查看type-fest.md
完整的TypeBox验证器文档,请查看typebox.md。请注意,我们通常使用AJV作为标准验证器,但TypeBox作为Schema生成器。