typescript-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript Patterns Skill

TypeScript模式最佳实践

Best practices for types, interfaces, assertions, and type safety.
TypeScript类型、接口、断言与类型安全的最佳实践。

Type Inference Over Explicit Returns

优先使用类型推断,而非显式返回类型

typescript
// ✅ Inferred return type
function calculateTotal(items: OrderItem[]) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

// ❌ Explicit return type
function calculateTotal(items: OrderItem[]): number {
    return items.reduce((sum, item) => sum + item.price, 0);
}
Why: Inference catches implicit type coercion bugs.
typescript
// ✅ 推断返回类型
function calculateTotal(items: OrderItem[]) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

// ❌ 显式返回类型
function calculateTotal(items: OrderItem[]): number {
    return items.reduce((sum, item) => sum + item.price, 0);
}
原因: 类型推断可以捕获隐式类型转换的bug。

Runtime Type Assertions

运行时类型断言

Never hard cast from
JSON.parse
. Validate at runtime.
typescript
// ❌ Hard cast
const value: MyType = JSON.parse(message);

// ✅ Runtime assertion
function isMyType(value: unknown): value is MyType {
    return typeof value === 'object' &&
        value !== null &&
        typeof (<MyType>value).prop === 'string';
}

const value = JSON.parse(message);
assert(isMyType(value), 'Invalid message format');
永远不要对
JSON.parse
的结果进行强制类型转换。要在运行时进行验证。
typescript
// ❌ 强制类型转换
const value: MyType = JSON.parse(message);

// ✅ 运行时断言
function isMyType(value: unknown): value is MyType {
    return typeof value === 'object' &&
        value !== null &&
        typeof (<MyType>value).prop === 'string';
}

const value = JSON.parse(message);
assert(isMyType(value), 'Invalid message format');

Type Assertion Functions

类型断言函数

typescript
// Parameter: 'value' with 'unknown' type
// Always return boolean, never throw
function isStrategy(value: unknown): value is Strategy {
    return typeof value === 'object' &&
        value !== null &&
        typeof (<Strategy>value).name === 'string';
}

// Use with assert
assert(isStrategy(value), 'Value is not a valid Strategy');
typescript
// 参数:类型为'unknown'的'value'
// 始终返回布尔值,永不抛出异常
function isStrategy(value: unknown): value is Strategy {
    return typeof value === 'object' &&
        value !== null &&
        typeof (<Strategy>value).name === 'string';
}

// 结合assert使用
assert(isStrategy(value), 'Value is not a valid Strategy');

Types vs Interfaces

类型(Types)vs 接口(Interfaces)

Always prefer types unless explicitly told otherwise.
typescript
// ✅ Type for object shapes
export type Product = {
    id: string;
    name: string;
    price: number;
};

// ✅ Type for local function-scoped types
function validate(input: unknown) {
    type Result = { errors: string[]; valid: boolean };
    const output: Result = { errors: [], valid: true };
    // ...
}

// ✅ Type for unions
type Status = 'pending' | 'active' | 'inactive';

// ❌ Interface (only use when explicitly requested)
export interface Product {
    id: string;
    name: string;
}
除非有明确要求,否则始终优先使用类型(Types)。
typescript
// ✅ 用类型定义对象结构
export type Product = {
    id: string;
    name: string;
    price: number;
};

// ✅ 用类型定义函数作用域内的局部类型
function validate(input: unknown) {
    type Result = { errors: string[]; valid: boolean };
    const output: Result = { errors: [], valid: true };
    // ...
}

// ✅ 用类型定义联合类型
type Status = 'pending' | 'active' | 'inactive';

// ❌ 接口(仅在明确要求时使用)
export interface Product {
    id: string;
    name: string;
}

Casting Syntax

类型转换语法

typescript
// ✅ Angle bracket syntax
const x = <number>y;
const config = <ConfigType>JSON.parse(json);

// ❌ 'as' syntax
const x = y as number;
typescript
// ✅ 尖括号语法
const x = <number>y;
const config = <ConfigType>JSON.parse(json);

// ❌ 'as'语法
const x = y as number;

Interface Conventions (Only When Explicitly Using Interfaces)

接口命名规范(仅在明确使用接口时遵循)

Note: Only apply these when explicitly told to use interfaces instead of types.
  • No
    I
    prefix or
    Data
    suffix
  • Properties in alphabetical order
  • Think of interfaces as nouns or adjectives (Shippable, Refundable)
  • When extending, inherit ALL properties (no
    Omit
    )
typescript
// Adjective interfaces
interface Shippable {
    shipping_address: string;
    shipping_cost: number;
}

// Concrete interface
interface Order extends Shippable {
    id: string;
    total: number;
}
注意: 仅在明确要求使用接口而非类型时,才遵循以下规范。
  • 不要使用
    I
    前缀或
    Data
    后缀
  • 属性按字母顺序排列
  • 将接口视为名词或形容词(如Shippable、Refundable)
  • 扩展接口时,继承所有属性(不要使用
    Omit
typescript
// 形容词类接口
interface Shippable {
    shipping_address: string;
    shipping_cost: number;
}

// 具体接口
interface Order extends Shippable {
    id: string;
    total: number;
}

Constants Over Enums

优先使用常量,而非枚举

Never use enums. Use const objects with
as const
instead.
typescript
// ✅ Const object with as const
export const TenantModel = {
    USER: 'user',
    ORGANIZATION: 'organization',
    EMPLOYER: 'employer'
} as const;

// Extract type from const
export type TenantModel = typeof TenantModel[keyof typeof TenantModel];

// ✅ Union type for simple cases
type Status = 'active' | 'inactive';

// ❌ Enum (never use)
export enum TenantModel {
    USER = 'user',
    ORGANIZATION = 'organization'
}

// Validate with Object.values
if (!Object.values(TenantModel).includes(model)) {
    throw new Error('Invalid model');
}
永远不要使用枚举。改用带有
as const
的常量对象。
typescript
// ✅ 带有as const的常量对象
export const TenantModel = {
    USER: 'user',
    ORGANIZATION: 'organization',
    EMPLOYER: 'employer'
} as const;

// 从常量中提取类型
export type TenantModel = typeof TenantModel[keyof typeof TenantModel];

// ✅ 简单场景下使用联合类型
type Status = 'active' | 'inactive';

// ❌ 枚举(永远不要使用)
export enum TenantModel {
    USER = 'user',
    ORGANIZATION = 'organization'
}

// 用Object.values进行验证
if (!Object.values(TenantModel).includes(model)) {
    throw new Error('Invalid model');
}

Iteration

迭代方式

typescript
// ✅ for...of loop
for (const item of items) {
    processItem(item);
}

// ❌ forEach
items.forEach((item) => {
    processItem(item);
});
Why:
for...of
works with
break
,
continue
,
return
,
await
, and has better debugging/stack traces.
Use
map
/
filter
/
reduce
for transformations, not side effects.
typescript
// ✅ for...of循环
for (const item of items) {
    processItem(item);
}

// ❌ forEach
items.forEach((item) => {
    processItem(item);
});
原因:
for...of
支持
break
continue
return
await
,且调试体验更好、栈追踪更清晰。
仅在进行数据转换时使用
map
/
filter
/
reduce
,不要用于处理副作用。

Import Style

导入风格

typescript
// ✅ Namespace imports
import * as mongodb from 'mongodb';
import * as Types from './types/index.js';

// ❌ Default imports
import MongoDB from 'mongodb';
typescript
// ✅ 命名空间导入
import * as mongodb from 'mongodb';
import * as Types from './types/index.js';

// ❌ 默认导入
import MongoDB from 'mongodb';

Organization

代码组织

  • Keep types with related code (not in
    types/
    directories)
  • Only export types that are part of public API
  • Use
    ReturnType
    and
    Parameters
    to access private types
  • 将类型与相关代码放在一起(不要放在
    types/
    目录中)
  • 仅导出属于公共API的类型
  • 使用
    ReturnType
    Parameters
    来访问私有类型