typescript-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Best Practices
TypeScript最佳实践
Type Safety
类型安全
- Enable in
strict: true— never disable strict checks in production code.tsconfig.json - Prefer over
unknown. Ifanyis unavoidable, add a comment explaining why and narrow it immediately.any - Use to validate a value matches a type while preserving its narrowed literal type:
satisfies
typescript
const config = {
endpoint: "/api/users",
timeout: 3000,
} satisfies Config;- Prefer type narrowing (type guards, operator,
in) over type assertions (instanceof).as
- 在中启用
tsconfig.json——生产代码中绝不要禁用严格检查。strict: true - 优先使用而非
unknown。如果必须使用any,添加注释说明原因并立即收窄其类型。any - 使用验证值是否匹配类型,同时保留其收窄后的字面量类型:
satisfies
typescript
const config = {
endpoint: "/api/users",
timeout: 3000,
} satisfies Config;- 优先使用类型收窄(类型守卫、操作符、
in)而非类型断言(instanceof)。as
Type Inference
类型推断
- Let TypeScript infer when the type is obvious — don't annotate what the compiler already knows:
typescript
// Redundant
const name: string = "Graham";
// Let it infer
const name = "Graham";- Always annotate function return types for exported/public functions — it catches accidental return type changes and improves IDE performance:
typescript
export function getUser(id: string): User | undefined {
return users.get(id);
}- Annotate function parameters — they cannot be inferred from implementation.
- 当类型显而易见时,让TypeScript自动推断——不要为编译器已能识别的类型添加注解:
typescript
// 冗余
const name: string = "Graham";
// 让编译器自动推断
const name = "Graham";- 对外导出/公开的函数必须标注返回类型——这能捕获意外的返回类型变更,并提升IDE性能:
typescript
export function getUser(id: string): User | undefined {
return users.get(id);
}- 为函数参数添加注解——它们无法从实现中自动推断。
Types vs Interfaces
类型与接口
- Use for unions, intersections, mapped types, and utility types.
type - Use for object shapes that may be extended or implemented.
interface - Be consistent within a codebase — pick one default and stick with it.
- 使用定义联合类型、交叉类型、映射类型和工具类型。
type - 使用定义可被扩展或实现的对象结构。
interface - 在代码库中保持一致性——选择一种默认方式并坚持使用。
Enums and Constants
枚举与常量
- Prefer objects over
as const:enum
typescript
const Status = {
Active: "active",
Inactive: "inactive",
} as const;
type Status = (typeof Status)[keyof typeof Status];- This gives you type safety, tree-shaking, and no runtime enum overhead.
- 优先使用对象而非
as const:enum
typescript
const Status = {
Active: "active",
Inactive: "inactive",
} as const;
type Status = (typeof Status)[keyof typeof Status];- 这种方式能提供类型安全、支持树摇,且无运行时枚举开销。
Null Handling
Null值处理
- Prefer explicit in types over optional properties when the distinction matters.
| undefined - Use optional chaining () and nullish coalescing (
?.) over manual null checks.?? - Avoid non-null assertions () — narrow the type instead.
!
- 当需要区分时,优先在类型中显式使用而非可选属性。
| undefined - 使用可选链()和空值合并运算符(
?.)替代手动空值检查。?? - 避免使用非空断言()——转而收窄类型。
!
Generics
泛型
- Name generic parameters descriptively when there are multiple: ,
TInputinstead ofTOutput,T.U - Constrain generics with to communicate intent:
extends
typescript
function merge<T extends Record<string, unknown>>(a: T, b: Partial<T>): T {
return { ...a, ...b };
}- Avoid over-genericizing — if a function only ever handles one type, don't make it generic.
- 当存在多个泛型参数时,使用描述性名称:比如、
TInput而非TOutput、T。U - 使用约束泛型以传达意图:
extends
typescript
function merge<T extends Record<string, unknown>>(a: T, b: Partial<T>): T {
return { ...a, ...b };
}- 避免过度泛型化——如果函数仅处理一种类型,不要将其设为泛型。
Utility Types
工具类型
- Use built-in utility types (,
Partial,Required,Pick,Omit,Record) instead of reimplementing them.Readonly - for data that should not be mutated.
Readonly<T> - and
Pickto derive subsets from existing types rather than duplicating fields.Omit
- 使用内置工具类型(、
Partial、Required、Pick、Omit、Record)而非自行实现。Readonly - 对不应被修改的数据使用。
Readonly<T> - 使用和
Pick从现有类型派生子集,而非重复定义字段。Omit
Error Handling
错误处理
- Type errors explicitly — don't rely on being
catch (e):any
typescript
try {
await fetchData();
} catch (error) {
if (error instanceof ApiError) {
handleApiError(error);
}
throw error;
}- Create typed error classes for domain-specific errors.
- 显式地为错误添加类型——不要依赖默认的
catch (e)类型:any
typescript
try {
await fetchData();
} catch (error) {
if (error instanceof ApiError) {
handleApiError(error);
}
throw error;
}- 为领域特定错误创建带类型的错误类。
Module Organization
模块组织
- One type/interface per concern — avoid monolithic files.
types.ts - Co-locate types with the code that uses them.
- Export types from barrel files only when they form part of the public API.
- Use /
import typefor type-only imports to enable proper tree-shaking.export type
- 每个关注点对应一个类型/接口——避免庞大的文件。
types.ts - 将类型与使用它们的代码放在一起。
- 仅当类型属于公共API时,从桶文件(barrel files)导出。
- 使用/
import type进行仅类型导入,以实现正确的树摇。export type
Naming Conventions
命名规范
- PascalCase for types, interfaces, enums, and classes.
- camelCase for variables, functions, and methods.
- UPPER_SNAKE_CASE for true constants (compile-time values).
- Don't prefix interfaces with or types with
I— it's not C#.T
- 类型、接口、枚举和类使用PascalCase命名。
- 变量、函数和方法使用camelCase命名。
- 真正的常量(编译时值)使用UPPER_SNAKE_CASE命名。
- 不要为接口添加前缀或为类型添加
I前缀——这不是C#。T