typescript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to Use
适用场景
Triggers: When writing TypeScript, defining types/interfaces, or using utility types.
Load when: writing TypeScript code, defining data structures, working with generics, or needing type safety patterns.
触发场景:编写 TypeScript 代码、定义类型/接口或使用工具类型时。
适用时机:编写 TypeScript 代码、定义数据结构、使用泛型或需要类型安全模式时。
Critical Patterns
核心模式
Pattern 1: Const Types (single source of truth)
模式1:常量类型(单一数据源)
typescript
// ✅ Create const object first, then extract type
const USER_ROLES = {
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest',
} as const;
type UserRole = typeof USER_ROLES[keyof typeof USER_ROLES];
// UserRole = 'admin' | 'user' | 'guest'
// ❌ Avoid: direct union types lose runtime values
type UserRole = 'admin' | 'user' | 'guest';typescript
// ✅ Create const object first, then extract type
const USER_ROLES = {
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest',
} as const;
type UserRole = typeof USER_ROLES[keyof typeof USER_ROLES];
// UserRole = 'admin' | 'user' | 'guest'
// ❌ Avoid: direct union types lose runtime values
type UserRole = 'admin' | 'user' | 'guest';Pattern 2: Flat Interfaces (one-level depth)
模式2:扁平化接口(单层结构)
typescript
// ✅ Flat, composable interfaces
interface Address {
street: string;
city: string;
country: string;
}
interface User {
id: string;
name: string;
address: Address; // Reference, not inline
}
interface Admin extends User {
permissions: string[];
}
// ❌ Avoid: deeply nested inline types
interface User {
address: {
street: string;
location: {
city: string;
coords: { lat: number; lng: number };
};
};
}typescript
// ✅ Flat, composable interfaces
interface Address {
street: string;
city: string;
country: string;
}
interface User {
id: string;
name: string;
address: Address; // Reference, not inline
}
interface Admin extends User {
permissions: string[];
}
// ❌ Avoid: deeply nested inline types
interface User {
address: {
street: string;
location: {
city: string;
coords: { lat: number; lng: number };
};
};
}Pattern 3: Avoid any
— Use unknown
or generics
anyunknown模式3:避免使用any
—— 改用unknown
或泛型
anyunknowntypescript
// ✅ Use unknown with type guard
function processData(data: unknown): string {
if (typeof data === 'string') return data;
if (typeof data === 'number') return data.toString();
throw new Error('Unsupported type');
}
// ✅ Use generics for flexible typing
function getFirst<T>(arr: T[]): T | undefined {
return arr[0];
}
// ❌ Avoid
function processData(data: any): any {
return data.toString(); // No type safety
}typescript
// ✅ Use unknown with type guard
function processData(data: unknown): string {
if (typeof data === 'string') return data;
if (typeof data === 'number') return data.toString();
throw new Error('Unsupported type');
}
// ✅ Use generics for flexible typing
function getFirst<T>(arr: T[]): T | undefined {
return arr[0];
}
// ❌ Avoid
function processData(data: any): any {
return data.toString(); // No type safety
}Code Examples
代码示例
Utility Types
Utility Types(工具类型)
typescript
interface User {
id: string;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Pick specific fields
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;
// Omit sensitive fields
type UserWithoutPassword = Omit<User, 'password'>;
// All fields optional (for updates)
type UserUpdate = Partial<User>;
// All fields required
type UserRequired = Required<User>;
// All fields readonly
type UserReadonly = Readonly<User>;
// Map of users
type UsersMap = Record<string, User>;
// Extract subset of union
type AdminOrUser = Extract<UserRole, 'admin' | 'user'>;
// Return type of function
type LoginResult = ReturnType<typeof loginUser>;
// Parameters of function
type LoginParams = Parameters<typeof loginUser>;typescript
interface User {
id: string;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Pick specific fields
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;
// Omit sensitive fields
type UserWithoutPassword = Omit<User, 'password'>;
// All fields optional (for updates)
type UserUpdate = Partial<User>;
// All fields required
type UserRequired = Required<User>;
// All fields readonly
type UserReadonly = Readonly<User>;
// Map of users
type UsersMap = Record<string, User>;
// Extract subset of union
type AdminOrUser = Extract<UserRole, 'admin' | 'user'>;
// Return type of function
type LoginResult = ReturnType<typeof loginUser>;
// Parameters of function
type LoginParams = Parameters<typeof loginUser>;Type Guards
Type Guards(类型守卫)
typescript
// ✅ Type guard with `is` syntax
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
// Discriminated union
type ApiResponse<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string };
function handleResponse<T>(response: ApiResponse<T>) {
if (response.status === 'success') {
console.log(response.data); // TypeScript knows data exists
} else {
console.error(response.message); // TypeScript knows message exists
}
}typescript
// ✅ Type guard with `is` syntax
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
// Discriminated union
type ApiResponse<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string };
function handleResponse<T>(response: ApiResponse<T>) {
if (response.status === 'success') {
console.log(response.data); // TypeScript knows data exists
} else {
console.error(response.message); // TypeScript knows message exists
}
}Import Types
Import Types(类型导入)
typescript
// ✅ Use import type for type-only imports
import type { User, UserRole } from './types';
import type { FC, ReactNode } from 'react';
// Runtime imports
import { useState } from 'react';
import { createUser } from './services/user';typescript
// ✅ Use import type for type-only imports
import type { User, UserRole } from './types';
import type { FC, ReactNode } from 'react';
// Runtime imports
import { useState } from 'react';
import { createUser } from './services/user';Readonly and Immutability
只读与不可变性
typescript
// ✅ Immutable data structures
const config: Readonly<{
apiUrl: string;
timeout: number;
}> = {
apiUrl: 'https://api.example.com',
timeout: 5000,
};
// Deep readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};typescript
// ✅ Immutable data structures
const config: Readonly<{
apiUrl: string;
timeout: number;
}> = {
apiUrl: 'https://api.example.com',
timeout: 5000,
};
// Deep readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};Anti-Patterns
反模式
❌ Using any
any❌ 使用any
anytypescript
// ❌ Bad
function parse(data: any) {
return data.value; // No type safety
}
// ✅ Good
function parse(data: unknown): string {
if (typeof data === 'object' && data !== null && 'value' in data) {
return String((data as { value: unknown }).value);
}
throw new Error('Invalid data shape');
}typescript
// ❌ Bad
function parse(data: any) {
return data.value; // No type safety
}
// ✅ Good
function parse(data: unknown): string {
if (typeof data === 'object' && data !== null && 'value' in data) {
return String((data as { value: unknown }).value);
}
throw new Error('Invalid data shape');
}❌ Non-null assertion without guard
❌ 无守卫的非空断言
typescript
// ❌ Bad - runtime crash if null
const user = getUser()!;
console.log(user.name);
// ✅ Good
const user = getUser();
if (!user) throw new Error('User not found');
console.log(user.name);typescript
// ❌ Bad - runtime crash if null
const user = getUser()!;
console.log(user.name);
// ✅ Good
const user = getUser();
if (!user) throw new Error('User not found');
console.log(user.name);Quick Reference
速查表
| Task | Pattern |
|---|---|
| Union from object | |
| Optional fields | |
| Pick fields | |
| Exclude fields | |
| Type guard | |
| Type-only import | |
| Readonly | |
| Generic constraint | |
| 任务 | 模式 |
|---|---|
| 从对象生成联合类型 | |
| 可选字段 | |
| 选取指定字段 | |
| 排除指定字段 | |
| 类型守卫 | |
| 仅类型导入 | |
| 只读 | |
| 泛型约束 | |
Rules
规则
- is forbidden — use
anywith type guards or generics;unknownis only acceptable as a last resort with an explanatory comment@ts-ignore - Use for type-only imports to ensure they are erased at compile time and do not affect the runtime bundle
import type - Prefer objects with
constover plain union types for enums and string literals — this preserves runtime values alongside the typeas const - Non-null assertions () require an immediately preceding null check; bare
!without a guard is a runtime crash waiting to happenvalue! - Interfaces should be flat and composable — deeply nested inline type definitions inside other types are a readability and reusability anti-pattern
- 禁止使用—— 改用带类型守卫的
any或泛型;仅在万不得已且添加解释性注释的情况下,才可使用unknown@ts-ignore - 仅类型导入时使用,确保其在编译时被移除,不影响运行时包体积
import type - 对于枚举和字符串字面量,优先选择带的常量对象而非普通联合类型 —— 这样可同时保留运行时值和类型
as const - 非空断言()必须紧跟空值检查;无守卫的
!可能导致运行时崩溃value! - 接口应扁平化且可组合 —— 在其他类型内部定义深度嵌套的内联类型是影响可读性和复用性的反模式