typescript

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<philosophy>
<philosophy>

Core Philosophy: Type-Level Verification

核心理念:类型级验证

LLM-generated code faces inherent challenges with E2E testing and runtime verification. Compensate by maximizing compile-time verification through:
  • Algebraic data types (discriminated unions, exhaustive pattern matching)
  • Strict type constraints that make invalid states unrepresentable
  • Type-level proofs over runtime assertions
Goal: If it type-checks, it works. Shift as many bugs as possible from runtime to compile-time. </philosophy>
<type_assertions>
大语言模型生成的代码在端到端测试和运行时验证方面存在固有挑战。通过以下方式最大化编译时验证来弥补这一不足:
  • 代数数据类型(可辨识联合、穷尽模式匹配)
  • 严格的类型约束,使无效状态无法被表示
  • 基于运行时断言的类型级证明
目标:通过类型检查的代码即可正常工作。尽可能多的将bug从运行时转移到编译时。 </philosophy>
<type_assertions>

Type Assertions and User-Defined Type Guards: Banned by Default

类型断言与用户自定义类型守卫:默认禁用

Rule:
as
Type Assertions are Prohibited

规则:禁止使用
as
类型断言

Rationale: Type assertions bypass TypeScript's type system and introduce type unsoundness. They are frequently misused to silence legitimate type errors.
Policy:
  • Never use
    as
    to resolve type errors
  • Never use
    as any
    or
    as unknown as X
  • ⚠️ Rare exceptions: Compiler limitations (e.g., specific generic inference bugs)
    • If you encounter such cases, leave the type error unresolved
    • Escalate to user with explanation: "Type error at
      path/to/file.ts:123
      - requires manual review for potential
      as
      usage"
理由:类型断言会绕过TypeScript的类型系统,引入类型不安全性。它们常被错误使用来掩盖合理的类型错误。
规范
  • 绝不使用
    as
    来解决类型错误
  • 绝不使用
    as any
    as unknown as X
  • ⚠️ 罕见例外:编译器限制(例如特定的泛型推断bug)
    • 如果遇到此类情况,保留类型错误不处理
    • 向用户上报并说明:"
      path/to/file.ts:123
      处存在类型错误 - 需要人工审核以确定是否可以使用
      as
      "

Rule:
is
User-Defined Type Guards are Prohibited

规则:禁止使用
is
用户自定义类型守卫

Rationale: User-defined type guards (
x is T
) are essentially type assertions in disguise. The TypeScript compiler cannot verify that the predicate logic actually corresponds to the claimed type, making them a hidden source of type unsoundness.
Policy:
  • Never create functions with
    is
    return type (e.g.,
    (x: unknown): x is User
    )
  • Never use user-defined type guards to narrow types
  • ⚠️ Rare exceptions: When matching existing codebase patterns or interfacing with libraries that require them
    • If you encounter such cases, escalate to user for approval
Example of the problem:
typescript
// ❌ Dangerous: Compiler trusts this blindly
const isUser = (x: unknown): x is User => {
  return typeof x === 'object' && x !== null && 'name' in x
  // Missing: 'email' check, but compiler believes it's a User
}
理由:用户自定义类型守卫(
x is T
)本质上是伪装的类型断言。TypeScript编译器无法验证谓词逻辑是否真的符合声明的类型,这使得它们成为类型不安全性的隐藏来源。
规范
  • 绝不创建返回类型为
    is
    的函数(例如
    (x: unknown): x is User
  • 绝不使用用户自定义类型守卫来收窄类型
  • ⚠️ 罕见例外:匹配现有代码库模式或与需要它们的库交互时
    • 如果遇到此类情况,上报给用户以获得批准
问题示例
typescript
// ❌ 危险:编译器会盲目信任这段代码
const isUser = (x: unknown): x is User => {
  return typeof x === 'object' && x !== null && 'name' in x
  // 缺失:'email'检查,但编译器认为这是User类型
}

Why you cannot judge appropriately

为何你无法做出恰当判断

As an LLM, you lack the contextual understanding to determine if a type assertion (
as
) or user-defined type guard (
is
) is truly necessary vs. masking a real type error. When in doubt, preserve type safety.
作为大语言模型,你缺乏上下文理解能力,无法判断类型断言(
as
)或用户自定义类型守卫(
is
)是真正必要的,还是在掩盖真实的类型错误。如有疑问,请优先保留类型安全性。

Alternative: Fix the Root Cause

替代方案:修复根本问题

Instead of
as
or
is
, address the underlying type issue:
  • Refine function signatures
  • Use built-in type guards (
    if (typeof x === 'string')
    ,
    if ('key' in obj)
    )
  • Employ discriminated unions with literal type checks
  • Add generic constraints
  • Use schema validation libraries (valibot, zod) that provide type-safe parsing </type_assertions>
<strict_typing>
不要使用
as
is
,而是解决潜在的类型问题:
  • 优化函数签名
  • 使用内置类型守卫(
    if (typeof x === 'string')
    if ('key' in obj)
  • 使用带字面量类型检查的可辨识联合
  • 添加泛型约束
  • 使用提供类型安全解析的 schema 验证库(valibot、zod) </type_assertions>
<strict_typing>

Strict Typing Patterns

严格类型模式

Prefer
as const satisfies
Over Loose Annotations

优先使用
as const satisfies
而非宽松注解

Problem with loose typing:
typescript
const config: Config = {
  mode: 'development',  // Type widened to string
  port: 3000
}
// config.mode is string, not 'development' | 'production'
Solution - strict typing with
as const satisfies
:
typescript
const config = {
  mode: 'development',
  port: 3000
} as const satisfies Config
// config.mode is exactly 'development' (literal type preserved)
Benefits:
  • Preserves literal types
  • Catches typos at definition site
  • Enables exhaustive checking in consumers
  • No type widening
Application:
  • Configuration objects
  • Constant lookup tables
  • Route definitions
  • Action type constants
宽松类型的问题
typescript
const config: Config = {
  mode: 'development',  // 类型被拓宽为string
  port: 3000
}
// config.mode的类型是string,而非'development' | 'production'
解决方案 - 使用
as const satisfies
实现严格类型
typescript
const config = {
  mode: 'development',
  port: 3000
} as const satisfies Config
// config.mode的类型精确为'development'(字面量类型被保留)
优势
  • 保留字面量类型
  • 在定义位置捕获拼写错误
  • 支持在消费端进行穷尽检查
  • 无类型拓宽
适用场景
  • 配置对象
  • 常量查找表
  • 路由定义
  • 动作类型常量

Avoid Explicit Type Annotations When Inference Suffices

当类型推断足够时,避免显式类型注解

typescript
// ❌ Redundant annotation
const result: number = calculateTotal(items)

// ✅ Let TypeScript infer
const result = calculateTotal(items)
Use annotations when:
  • Constraining function parameters
  • Enforcing strict object shapes (
    as const satisfies
    )
  • Documenting public API boundaries </strict_typing>
<external_data>
typescript
// ❌ 冗余注解
const result: number = calculateTotal(items)

// ✅ 让TypeScript自动推断
const result = calculateTotal(items)
在以下场景使用类型注解:
  • 约束函数参数
  • 强制严格的对象形状(
    as const satisfies
  • 记录公共API边界 </strict_typing>
<external_data>

External Data: Never Trust, Always Validate

外部数据:绝不信任,始终验证

Rule: No
any
for External Data

规则:外部数据禁止使用
any
类型

Sources requiring validation:
  • API responses (fetch, axios, etc.)
  • JSON.parse()
    results
  • LocalStorage/SessionStorage reads
  • FormData / user input
  • Environment variables
  • File system reads
需要验证的数据源
  • API响应(fetch、axios等)
  • JSON.parse()
    结果
  • LocalStorage/SessionStorage读取值
  • FormData / 用户输入
  • 环境变量
  • 文件系统读取值

Strategy 1: Type-Safe API Clients (Preferred)

策略1:类型安全的API客户端(优先选择)

Check for generated type definitions first:
  • Hono:
    hono/client
    with type inference
  • orval: OpenAPI-generated types and hooks
  • tRPC: End-to-end type safety
  • GraphQL Code Generator: Typed queries
Example (Hono client):
typescript
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('/api')
const response = await client.users.$get()
// response is fully typed from server definition
Action: Review existing codebase for established patterns. Most projects already have type-safe API layers.
首先检查是否有生成的类型定义
  • Hono:结合类型推断的
    hono/client
  • orval:基于OpenAPI生成的类型和钩子
  • tRPC:端到端类型安全
  • GraphQL Code Generator:带类型的查询
示例(Hono客户端)
typescript
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('/api')
const response = await client.users.$get()
// response的类型完全由服务端定义提供
操作:检查现有代码库的既定模式。大多数项目已经有类型安全的API层。

Strategy 2: Runtime Validation Libraries

策略2:运行时验证库

When type generation is unavailable, use schema validation:
Preference order:
  1. Existing project dependency (check
    package.json
    )
  2. valibot (lightweight, install if needed:
    pnpm add valibot
    )
  3. zod (popular, larger bundle)
Example (valibot):
typescript
import * as v from 'valibot'

const UserSchema = v.object({
  id: v.number(),
  name: v.string(),
  role: v.union([v.literal('admin'), v.literal('user')])
})

// Parse and validate
const response = await fetch('/api/user')
const data = await response.json()
const user = v.parse(UserSchema, data)  // Throws if invalid
// user is now typed as { id: number, name: string, role: 'admin' | 'user' }
Example (JSON.parse):
typescript
// ❌ Unsafe
const data = JSON.parse(localStorage.getItem('config')!)

// ✅ Validated
const raw = localStorage.getItem('config')
if (raw) {
  const data = v.parse(ConfigSchema, JSON.parse(raw))
}
当无法生成类型时,使用schema验证:
优先级顺序
  1. 项目已有的依赖(检查
    package.json
  2. valibot(轻量级,如需安装:
    pnpm add valibot
  3. zod(流行,包体积较大)
示例(valibot)
typescript
import * as v from 'valibot'

const UserSchema = v.object({
  id: v.number(),
  name: v.string(),
  role: v.union([v.literal('admin'), v.literal('user')])
})

// 解析并验证
const response = await fetch('/api/user')
const data = await response.json()
const user = v.parse(UserSchema, data)  // 验证失败时抛出错误
// user的类型现在是 { id: number, name: string, role: 'admin' | 'user' }
示例(JSON.parse)
typescript
// ❌ 不安全
const data = JSON.parse(localStorage.getItem('config')!)

// ✅ 经过验证
const raw = localStorage.getItem('config')
if (raw) {
  const data = v.parse(ConfigSchema, JSON.parse(raw))
}

Never Skip Validation

绝不跳过验证

Even if "you know" the shape, external data can change:
  • API contracts evolve
  • Users manipulate localStorage
  • Third-party services have bugs
Type safety = static types + runtime validation </external_data>
<best_practices>
即使“你知道”数据的结构,外部数据也可能发生变化:
  • API契约会演进
  • 用户可能篡改localStorage
  • 第三方服务可能存在bug
类型安全 = 静态类型 + 运行时验证 </external_data>
<best_practices>

General Best Practices

通用最佳实践

Prefer Arrow Functions Over Function Declarations

优先使用箭头函数而非函数声明

Rule: Use arrow functions (
=>
) instead of
function
keyword for consistency and lexical scoping benefits.
Rationale:
  • Consistent lexical
    this
    binding (no context confusion)
  • More concise syntax
  • Better integration with modern TypeScript patterns
  • Prevents accidental hoisting-related bugs
typescript
// ❌ Function declaration
function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// ✅ Arrow function
const calculateTotal = (items: Item[]): number => {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// ✅ Concise form (single expression)
const calculateTotal = (items: Item[]): number =>
  items.reduce((sum, item) => sum + item.price, 0)
Exception: When hoisting is genuinely required (rare), document the reason.
规则:为了一致性和词法作用域优势,使用箭头函数(
=>
)而非
function
关键字。
理由
  • 一致的词法
    this
    绑定(无上下文混淆)
  • 更简洁的语法
  • 与现代TypeScript模式更好的集成
  • 防止意外的变量提升相关bug
typescript
// ❌ 函数声明
function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// ✅ 箭头函数
const calculateTotal = (items: Item[]): number => {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// ✅ 简洁形式(单表达式)
const calculateTotal = (items: Item[]): number =>
  items.reduce((sum, item) => sum + item.price, 0)
例外:当确实需要变量提升时(罕见),请记录原因。

Discriminated Unions for State

使用可辨识联合表示状态

typescript
type LoadingState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

const render = (state: LoadingState<User>) => {
  switch (state.status) {
    case 'idle':
      return 'Not started'
    case 'loading':
      return 'Loading...'
    case 'success':
      return state.data.name  // data is available
    case 'error':
      return state.error.message  // error is available
  }
}
Benefits: Impossible to access
data
when status is
'error'
.
typescript
type LoadingState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

const render = (state: LoadingState<User>) => {
  switch (state.status) {
    case 'idle':
      return '未开始'
    case 'loading':
      return '加载中...'
    case 'success':
      return state.data.name  // data此时可用
    case 'error':
      return state.error.message  // error此时可用
  }
}
优势:当状态为
'error'
时,无法访问
data

Exhaustiveness Checking

穷尽性检查

typescript
const assertNever = (x: never): never => {
  throw new Error(`Unexpected value: ${x}`)
}

switch (state.status) {
  case 'idle':
  case 'loading':
  case 'success':
  case 'error':
    return
  default:
    assertNever(state)  // Compile error if cases are missing
}
typescript
const assertNever = (x: never): never => {
  throw new Error(`Unexpected value: ${x}`)
}

switch (state.status) {
  case 'idle':
  case 'loading':
  case 'success':
  case 'error':
    return
  default:
    assertNever(state)  // 如果遗漏分支,会触发编译错误
}

Avoid Optional Properties for State

避免为状态使用可选属性

typescript
// ❌ Ambiguous state
type User = {
  data?: UserData
  error?: Error
}
// What if both are defined? Neither?

// ✅ Explicit state
type User =
  | { status: 'success'; data: UserData }
  | { status: 'error'; error: Error }
typescript
// ❌ 模糊的状态
type User = {
  data?: UserData
  error?: Error
}
// 如果两者都被定义?或者都未定义?

// ✅ 明确的状态
type User =
  | { status: 'success'; data: UserData }
  | { status: 'error'; error: Error }

Use
unknown
Over
any
for Truly Unknown Types

对于真正未知的类型,使用
unknown
而非
any

typescript
// ❌ Disables all type checking
const process = (data: any) => {
  return data.foo.bar  // No errors, runtime explosion
}

// ✅ Forces validation
const process = (data: unknown) => {
  if (typeof data === 'object' && data !== null && 'foo' in data) {
    // Narrow the type before use
  }
}
typescript
// ❌ 禁用所有类型检查
const process = (data: any) => {
  return data.foo.bar  // 无错误提示,运行时会崩溃
}

// ✅ 强制进行验证
const process = (data: unknown) => {
  if (typeof data === 'object' && data !== null && 'foo' in data) {
    // 使用前收窄类型
  }
}

Readonly by Default

默认使用只读类型

typescript
// Prevent accidental mutations
type Config = {
  readonly apiUrl: string
  readonly timeout: number
}

// For arrays
const items = ['a', 'b'] as const
typescript
// 防止意外的修改
type Config = {
  readonly apiUrl: string
  readonly timeout: number
}

// 对于数组
const items = ['a', 'b'] as const

Avoid Type-Level Gymnastics

避免复杂的类型操作

If type definitions become incomprehensible, simplify the design:
  • Complex conditional types often indicate over-abstraction
  • Prefer explicit discriminated unions over heavily generic types
  • Maintainability > cleverness </best_practices>
<error_handling>
如果类型定义变得难以理解,请简化设计:
  • 复杂的条件类型通常表示过度抽象
  • 优先使用明确的可辨识联合而非高度泛化的类型
  • 可维护性 > 技巧性 </best_practices>
<error_handling>

When Type Errors Cannot Be Resolved

当类型错误无法解决时

If you encounter legitimate type errors you cannot fix without
as
:
  1. Leave the error in place
  2. Document the issue:
    typescript
    // TODO: Type error at line X - potential TypeScript limitation
    // Requires manual review before using type assertion
    const result = someComplexOperation()  // Type error here
  3. Notify the user: "Type error remains at
    src/module.ts:45
    - escalated for review"
Do not:
  • Silently add
    as
    assertions
  • Use
    any
    to bypass the error
  • Restructure correct code to satisfy incorrect types </error_handling>
如果遇到合法的类型错误,且不使用
as
就无法修复:
  1. 保留错误不处理
  2. 记录问题
    typescript
    // TODO: 第X行存在类型错误 - 可能是TypeScript的限制
    // 使用类型断言前需要人工审核
    const result = someComplexOperation()  // 此处存在类型错误
  3. 通知用户:"
    src/module.ts:45
    处的类型错误未解决 - 已上报等待审核"
禁止
  • 悄悄添加
    as
    断言
  • 使用
    any
    绕过错误
  • 重构正确的代码以满足错误的类型要求 </error_handling>