quality-code
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWriting quality full-stack TypeScript
编写高质量全栈TypeScript
Apply these principles when writing or reviewing TypeScript code.
编写或评审TypeScript代码时,请遵循以下原则。
Make impossible states unrepresentable
让不可能的状态无法被表示
Use the type system to make invalid states fail at compile time. Fewer reachable states = easier code to read and change.
利用类型系统让无效状态在编译阶段就报错。可到达的状态越少,代码越易于阅读和修改。
Branded types
Branded类型
Brand primitives so they can't be mixed up. Validate once at the boundary; downstream code trusts the type.
ts
type PhoneNumber = string & { __brand: "PhoneNumber" };
function parsePhone(input: string): PhoneNumber {
if (!/^\+?\d{10,15}$/.test(input)) throw new Error(`Invalid: ${input}`);
return input as PhoneNumber;
}
function sendSMS(to: PhoneNumber, body: string) {
/* input is trusted */
}If the project already uses a library with native branded-type support (e.g. Effect), use their primitives instead of rolling your own.
为原始类型添加品牌标识,避免混淆。在边界处验证一次;下游代码可信任该类型。
ts
type PhoneNumber = string & { __brand: "PhoneNumber" };
function parsePhone(input: string): PhoneNumber {
if (!/^\+?\d{10,15}$/.test(input)) throw new Error(`Invalid: ${input}`);
return input as PhoneNumber;
}
function sendSMS(to: PhoneNumber, body: string) {
/* input is trusted */
}如果项目已使用原生支持Branded类型的库(如Effect),请使用其原生类型而非自行实现。
Discriminated unions over flag bags
使用区分联合类型而非标记集合
ts
// Don't — invalid combos representable
type State = { loading: boolean; user?: User; error?: string };
// Do — only valid states exist
type State =
| { status: "loading" }
| { status: "success"; user: User }
| { status: "error"; error: string };ts
// 不推荐——可表示无效组合
type State = { loading: boolean; user?: User; error?: string };
// 推荐——仅存在有效状态
type State =
| { status: "loading" }
| { status: "success"; user: User }
| { status: "error"; error: string };Let the types flow end-to-end
让类型端到端流转
DB schema → server → client should share types without manual duplication. Use whatever end-to-end type tool the project already has (tRPC, oRPC, Elysia, TanStack Start). A branded as should arrive on the client still branded.
users.emailEmailDon't restate types you can derive. Reach for , , , , , etc. before writing a new interface. For function arguments, infer from the source instead of typing them by hand:
PickOmitParametersReturnTypeAwaitedtypeofts
// Don't — duplicate shape, drifts when the row changes
type UserSummary = { id: string; email: Email };
function renderUser(u: UserSummary) {
/* ... */
}
// Do — derive from the source of truth
type User = Awaited<ReturnType<typeof db.query.users.findFirst>>;
function renderUser(u: Pick<User, "id" | "email">) {
/* ... */
}数据库架构→服务器→客户端应共享类型,无需手动重复定义。使用项目已有的端到端类型工具(tRPC、oRPC、Elysia、TanStack Start)。被标记为的在客户端应仍保持该品牌标识。
Emailusers.email不要重复声明可推导的类型。在编写新接口前,优先使用、、、、、等工具类型。对于函数参数,从数据源推导类型而非手动编写:
PickOmitParametersReturnTypeAwaitedtypeofts
// 不推荐——重复结构,当数据行变化时会不一致
type UserSummary = { id: string; email: Email };
function renderUser(u: UserSummary) {
/* ... */
}
// 推荐——从可信数据源推导
type User = Awaited<ReturnType<typeof db.query.users.findFirst>>;
function renderUser(u: Pick<User, "id" | "email">) {
/* ... */
}Pass objects, not positional args
传递对象而非位置参数
ts
// Don't — swap two args, still compiles
sendEmail("Welcome!", "Hi there");
// Do — order-independent, self-documenting
sendEmail({ to: "alice@x.com", body: "Hi there" });Skip on hot perf-critical paths; use elsewhere by default.
ts
// 不推荐——交换两个参数仍能编译通过
sendEmail("Welcome!", "Hi there");
// 推荐——与顺序无关,自文档化
sendEmail({ to: "alice@x.com", body: "Hi there" });在性能关键的热路径上可跳过此原则;其他场景默认遵循。
Standard Schema for shared validation
共享验证的标准 Schema
For libraries or code that doesn't want to pick a validator, accept .
StandardSchemaV1<unknown, T>对于不想选择特定验证器的库或代码,接受类型。
StandardSchemaV1<unknown, T>Tests as real as possible
测试尽可能贴近真实场景
Don't mock things you can run. Spin up real services:
- LocalStack for AWS
- Miniflare for Cloudflare Workers
- Real Postgres/SQLite (e.g. ), not a mock DB
bun:sqlite
Mock only third-party services that have no test environment.
不要模拟可直接运行的组件。启动真实服务:
- 使用LocalStack模拟AWS服务
- 使用Miniflare模拟Cloudflare Workers
- 使用真实的Postgres/SQLite(如),而非模拟数据库
bun:sqlite
仅在第三方服务无测试环境时才进行模拟。
OpenTelemetry, not print logging
使用OpenTelemetry而非打印日志
When adding observability, instrument with OTel spans. The setup cost pays back the first time a user sends a request ID and you can answer instead of guess.
添加可观测性时,使用OTel Span进行埋点。当用户首次发送请求ID,你无需猜测即可排查问题时,前期的配置成本就得到了回报。