typescript-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Best Practices
TypeScript最佳实践
Follows type-first, functional, and error handling patterns from CLAUDE.md. This skill covers language-specific idioms only.
遵循CLAUDE.md中的类型优先、函数式编程和错误处理模式。本技能仅涵盖特定语言的惯用写法。
Pair with React Best Practices
搭配React最佳实践使用
When working with React components (, files or imports), always load alongside this skill. This skill covers TypeScript fundamentals; React-specific patterns (effects, hooks, refs, component design) are in the dedicated React skill.
.tsx.jsx@reactreact-best-practices在处理React组件(、文件或导入)时,请始终同时加载技能。本技能涵盖TypeScript基础内容;React特定模式(副作用、hooks、refs、组件设计)在专门的React技能中介绍。
.tsx.jsx@reactreact-best-practicesMake Illegal States Unrepresentable
使非法状态无法被表示
Use the type system to prevent invalid states at compile time.
Discriminated unions for mutually exclusive states:
ts
// Good: only valid combinations possible
type RequestState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
// Bad: allows invalid combinations like { loading: true, error: Error }
type RequestState<T> = {
loading: boolean;
data?: T;
error?: Error;
};Branded types for domain primitives:
ts
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
// Compiler prevents passing OrderId where UserId expected
function getUser(id: UserId): Promise<User> { /* ... */ }Const assertions for literal unions:
ts
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
// Array and type stay in sync automatically
function isValidRole(role: string): role is Role {
return ROLES.includes(role as Role);
}Exhaustive switch with never check:
ts
type Status = "active" | "inactive";
function processStatus(status: Status): string {
switch (status) {
case "active":
return "processing";
case "inactive":
return "skipped";
default: {
const _exhaustive: never = status;
throw new Error(`unhandled status: ${_exhaustive}`);
}
}
}使用类型系统在编译时防止无效状态。
使用可辨识联合处理互斥状态:
ts
// Good: only valid combinations possible
type RequestState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
// Bad: allows invalid combinations like { loading: true, error: Error }
type RequestState<T> = {
loading: boolean;
data?: T;
error?: Error;
};为领域原语使用品牌化类型:
ts
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
// Compiler prevents passing OrderId where UserId expected
function getUser(id: UserId): Promise<User> { /* ... */ }为字面量联合使用const断言:
ts
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
// Array and type stay in sync automatically
function isValidRole(role: string): role is Role {
return ROLES.includes(role as Role);
}带never检查的穷尽式switch语句:
ts
type Status = "active" | "inactive";
function processStatus(status: Status): string {
switch (status) {
case "active":
return "processing";
case "inactive":
return "skipped";
default: {
const _exhaustive: never = status;
throw new Error(`unhandled status: ${_exhaustive}`);
}
}
}Runtime Validation with Zod
使用Zod进行运行时验证
- Define schemas as single source of truth; infer TypeScript types with . Avoid duplicating types and schemas.
z.infer<> - Use for user input where failure is expected; use
safeParseat trust boundaries where invalid data is a bug.parse - Compose schemas with ,
.extend(),.pick(),.omit()for DRY definitions..merge() - Add for data normalization at parse time (trim strings, parse dates).
.transform()
ts
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
createdAt: z.string().transform((s) => new Date(s)),
});
type User = z.infer<typeof UserSchema>;
// Strict parsing at trust boundaries — throws if API contract violated
export async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`fetch user ${id} failed: ${response.status}`);
}
return UserSchema.parse(await response.json());
}
// Caller handles both success and error from user input
const result = UserSchema.safeParse(formData);
if (!result.success) {
setErrors(result.error.flatten().fieldErrors);
return;
}- 将Schema定义作为单一事实来源;使用推导TypeScript类型。避免重复定义类型和Schema。
z.infer<> - 在预期可能失败的用户输入场景中使用;在信任边界处使用
safeParse,此处无效数据属于bug。parse - 使用、
.extend()、.pick()、.omit()组合Schema,实现DRY(Don't Repeat Yourself)定义。.merge() - 添加在解析时进行数据标准化(如修剪字符串、解析日期)。
.transform()
ts
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
createdAt: z.string().transform((s) => new Date(s)),
});
type User = z.infer<typeof UserSchema>;
// Strict parsing at trust boundaries — throws if API contract violated
export async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`fetch user ${id} failed: ${response.status}`);
}
return UserSchema.parse(await response.json());
}
// Caller handles both success and error from user input
const result = UserSchema.safeParse(formData);
if (!result.success) {
setErrors(result.error.flatten().fieldErrors);
return;
}Optional: type-fest
可选工具:type-fest
For advanced type utilities beyond TypeScript builtins, consider type-fest:
- - cleaner branded types than manual
Opaque<T, Token>pattern& { __brand } - - recursive partial for nested objects
PartialDeep<T> - - recursive readonly for immutable data
ReadonlyDeep<T> - /
SetRequired<T, K>- targeted field modificationsSetOptional<T, K> - - flatten complex intersection types in IDE tooltips
Simplify<T>
ts
import type { Opaque, PartialDeep } from 'type-fest';
type UserId = Opaque<string, 'UserId'>;
type UserPatch = PartialDeep<User>;对于TypeScript内置工具之外的高级类型工具,可以考虑type-fest:
- - 比手动
Opaque<T, Token>模式更简洁的品牌化类型& { __brand } - - 针对嵌套对象的递归Partial类型
PartialDeep<T> - - 针对不可变数据的递归Readonly类型
ReadonlyDeep<T> - /
SetRequired<T, K>- 针对性的字段修改SetOptional<T, K> - - 在IDE提示中展平复杂的交叉类型
Simplify<T>
ts
import type { Opaque, PartialDeep } from 'type-fest';
type UserId = Opaque<string, 'UserId'>;
type UserPatch = PartialDeep<User>;