typescript-and-react-guidelines

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Coding Standards & Best Practices

编码规范与最佳实践

Non-negotiable rules

不可妥协的规则

Apply non-negotiable rules (19 rules, ordered by impact) before anything else.

在进行任何操作前,先应用不可妥协的规则(共19条,按影响程度排序)。

Agent Instructions

Agent 操作说明

After reading this skill:
  1. Apply ALL rules to every file you create or modify
  2. Run the Pre-output Validation checklist before returning any code
  3. If a rule conflicts with a user request, flag it explicitly and propose a compliant alternative
  4. Reference specific rule names when explaining choices (e.g., "Per the KISS principle, I simplified this by...")
  5. Load example files on demand — only read the relevant file for the task at hand

阅读本技能后:
  1. 对所有创建或修改的文件应用全部规则
  2. 在返回任何代码前,运行输出前校验清单
  3. 如果规则与用户需求冲突,明确标记冲突并提出符合规范的替代方案
  4. 解释选择时引用具体规则名称(例如:“根据KISS原则,我通过...简化了这段代码”)
  5. 按需加载示例文件 —— 仅读取与当前任务相关的文件

Code Quality Principles

代码质量原则

PrincipleRule
Readability FirstCode is read more than written. Clear names > clever code.
KISSSimplest solution that works. Avoid over-engineering.
Avoid GodComponentSingle responsibility per component; extract utilities, hooks, sub-components.
DRYExtract common logic. Create reusable components.
YAGNIDon't build features before they're needed.
ImmutabilityNever mutate — always return new values.

原则规则
可读性优先代码读的次数远多于写的次数。清晰的命名优于炫技的代码。
KISS采用最简单的可行方案。避免过度设计。
避免上帝组件每个组件单一职责;提取工具函数、Hooks、子组件。
DRY提取通用逻辑。创建可复用组件。
YAGNI不要提前构建尚未需要的功能。
不可变性绝不直接修改数据 —— 始终返回新值。

Avoiding GodComponent (decomposition strategy)

避免上帝组件(拆分策略)

Apply the following order to keep components focused and testable:
  1. Extract pure TypeScript utilities first
    • Move logic that has no React dependency into pure functions.
    • If a utility takes more than one argument, use object destructuring in the signature so argument names are explicit at the call site. Extract the parameter type (e.g.
      interface FormatRangeArgs { start: number; end: number }
      then
      const formatRange = ({ start, end }: FormatRangeArgs) => ...
      ).
    • Reusable across features → put in
      src/utils/xyz.utils.ts
      .
    • Feature-specific → keep next to the component as
      component-name.utils.ts
      (same kebab-case base name as the component file, e.g.
      market-list-item.utils.ts
      next to
      market-list-item.tsx
      ).
  2. Extract logic into named hooks
    • Move state, effects, and derived logic into hooks (e.g.
      use-xyz.ts
      ).
    • Reusable across features → put in
      src/hooks/use-xyz.ts
      .
    • Feature-specific → keep in the feature’s
      hooks/
      subdirectory (e.g.
      features/market-list/hooks/use-market-filters.ts
      ).
  3. Split the visual layer into sub-components
    • If the render/JSX exceeds roughly 40 lines, extract sub-components with clear props and a single responsibility.
    • Each sub-component should have its own props interface and live in its own file (or a dedicated subfolder) when it grows.

按照以下顺序操作,保持组件聚焦且可测试:
  1. 优先提取纯TypeScript工具函数
    • 将不依赖React的逻辑移至纯函数中。
    • 如果工具函数有多个参数,在签名中使用对象解构,以便调用处明确参数名称。提取参数类型(例如:
      interface FormatRangeArgs { start: number; end: number }
      ,然后
      const formatRange = ({ start, end }: FormatRangeArgs) => ...
      )。
    • 跨功能复用 → 放入
      src/utils/xyz.utils.ts
    • 特定功能专用 → 与组件放在同一目录下,命名为
      component-name.utils.ts
      (与组件文件使用相同的短横线分隔命名,例如
      market-list-item.utils.ts
      market-list-item.tsx
      放在一起)。
  2. 将逻辑提取为命名Hooks
    • 将状态、副作用和派生逻辑移至Hooks中(例如
      use-xyz.ts
      )。
    • 跨功能复用 → 放入
      src/hooks/use-xyz.ts
    • 特定功能专用 → 放在对应功能的
      hooks/
      子目录中(例如
      features/market-list/hooks/use-market-filters.ts
      )。
  3. 将视觉层拆分为子组件
    • 如果渲染/JSX代码超过约40行,提取具有明确Props和单一职责的子组件。
    • 每个子组件应有自己的Props接口,当代码增长时,应放在独立文件(或专用子文件夹)中。

Sections & Example Files

章节与示例文件

TypeScript / JavaScript

TypeScript / JavaScript

TopicExample FileWhen to load
Variable & function naming (incl. boolean prefixes)
examples/typescript/naming.ts
When naming anything (arrow functions only; booleans: is/has/should/can/will)
Immutability patterns
examples/typescript/immutability.ts
When working with state/objects/arrays
Error handling
examples/typescript/error-handling.ts
When writing async code
Async / Promise patterns
examples/typescript/async-patterns.ts
When using await/Promise
Type safety
examples/typescript/type-safety.ts
When defining interfaces/types (no inline types; no nested types; extract named types)
Control flow & readability
examples/typescript/control-flow.ts
Early returns, const vs let, Array.includes/some, nullish coalescing, destructuring
主题示例文件加载时机
变量与函数命名(含布尔值前缀)
examples/typescript/naming.ts
命名任何内容时(仅箭头函数;布尔值:is/has/should/can/will前缀)
不可变性模式
examples/typescript/immutability.ts
处理状态/对象/数组时
错误处理
examples/typescript/error-handling.ts
编写异步代码时
异步 / Promise 模式
examples/typescript/async-patterns.ts
使用await/Promise时
类型安全
examples/typescript/type-safety.ts
定义接口/类型时(不使用内联类型;不使用嵌套类型;提取命名类型)
控制流与可读性
examples/typescript/control-flow.ts
提前返回、const vs let、Array.includes/some、空值合并、解构

React

React

TopicExample FileWhen to load
Component structure
examples/react/component-structure.tsx
When creating a component
主题示例文件加载时机
组件结构
examples/react/component-structure.tsx
创建组件时

Testing

测试

TopicExample FileWhen to load
Unit test patterns
examples/testing/unit-testing-patterns.tsx
When writing Jest/RTL tests (AAA, screen, spyOn, it.each, getByRole, mock factory)
主题示例文件加载时机
单元测试模式
examples/testing/unit-testing-patterns.tsx
编写Jest/RTL测试时(AAA模式、screen、spyOn、it.each、getByRole、模拟工厂)

Anti-patterns (read during code review)

反模式(代码审查时阅读)

TopicFile
All BAD patterns grouped
anti-patterns/what-not-to-do.ts
Code smells detection
anti-patterns/code-smells.ts

主题文件
汇总所有不良模式
anti-patterns/what-not-to-do.ts
代码异味检测
anti-patterns/code-smells.ts

File Organization Rules

文件组织规则

  • 200–400 lines typical file length
  • 800 lines absolute maximum
  • One responsibility per file (high cohesion, low coupling)
  • File names: always kebab-case (lowercase with hyphens). No PascalCase or camelCase in file or folder names.
components/button.tsx           # kebab-case (not Button.tsx)
hooks/use-auth.ts               # kebab-case (not useAuth.ts)
lib/format-date.ts              # kebab-case (not formatDate.ts)
types/market.types.ts           # kebab-case + optional .types / .utils / .store suffix
features/market-list/market-list-item.tsx
settings-screen.tsx              # e.g. settings-screen.tsx, use-device-discovery.ts
Components and hooks are still exported with PascalCase (components) or camelCase with
use
prefix (hooks); only the file name is kebab-case.

  • 典型文件长度 200–400行
  • 绝对最大长度 800行
  • 每个文件单一职责(高内聚,低耦合)
  • 文件名:始终使用短横线分隔命名(小写字母加连字符)。文件名或文件夹名不使用PascalCase或camelCase。
components/button.tsx           # 短横线分隔命名(而非Button.tsx)
hooks/use-auth.ts               # 短横线分隔命名(而非useAuth.ts)
lib/format-date.ts              # 短横线分隔命名(而非formatDate.ts)
types/market.types.ts           # 短横线分隔命名 + 可选的.types / .utils / .store后缀
features/market-list/market-list-item.tsx
settings-screen.tsx              # 例如settings-screen.tsx、use-device-discovery.ts
组件和Hooks的导出仍使用PascalCase(组件)或带
use
前缀的camelCase(Hooks);仅文件名使用短横线分隔命名。

Code Style / TypeScript

代码风格 / TypeScript

  • TypeScript strict mode — enable in
    tsconfig.json
    for maximum type safety.
  • Explicit function signatures — type function parameters and return types explicitly; avoid relying on inference for public APIs.
  • Type inference for locals — prefer inference for local variables when the type is obvious (e.g.
    const count = 0
    ).

  • TypeScript严格模式 —— 在
    tsconfig.json
    中启用,以获得最高类型安全性。
  • 显式函数签名 —— 显式为函数参数和返回值指定类型;公共API避免依赖类型推断。
  • 局部变量使用类型推断 —— 当类型明显时,局部变量优先使用类型推断(例如
    const count = 0
    )。

React Components

React 组件

  • FunctionComponent — type React components with
    FunctionComponent<Props>
    (or
    FC<Props>
    ); use typed props interfaces, not inline or
    any
    .
  • Early returns — use early returns in component bodies to keep the main render path flat and readable.
  • Fragment shorthand — use
    <>...</>
    instead of
    <Fragment>
    unless a
    key
    is required.
  • Exports — prefer named exports for components; default export only when required by the framework (e.g. Expo Router).

  • FunctionComponent —— 使用
    FunctionComponent<Props>
    (或
    FC<Props>
    )为React组件指定类型;使用带类型的Props接口,不使用内联类型或
    any
  • 提前返回 —— 在组件主体中使用提前返回,保持主渲染路径扁平且易读。
  • Fragment简写 —— 除非需要
    key
    ,否则使用
    <>...</>
    而非
    <Fragment>
  • 导出 —— 组件优先使用命名导出;仅当框架要求时使用默认导出(例如Expo Router)。

React Hooks

React Hooks

  • Functions vs hooks — prefer a plain function to a custom hook when you don't need React primitives (state, effects, context).
  • use prefix — use the
    use
    prefix only for real hooks; never for plain functions.
  • useMemo / useCallback — avoid for simple computations or callbacks; use when profiling shows a need or when passing callbacks to memoized children.
  • Handlers — use a single arrow function per handler (e.g.
    const handleClick = () => { ... }
    ); avoid function factories that return handlers.
  • Selected items — store selection by ID in state and derive the full item from the list (e.g.
    selectedItem = items.find(i => i.id === selectedId)
    ); avoids stale references when the list updates.
  • 函数与Hooks的选择 —— 当不需要React原语(状态、副作用、上下文)时,优先使用纯函数而非自定义Hook。
  • use前缀 —— 仅对真正的Hooks使用
    use
    前缀;纯函数绝不使用该前缀。
  • useMemo / useCallback —— 简单计算或回调避免使用;仅当性能分析显示需要,或向已记忆的子组件传递回调时使用。
  • 事件处理函数 —— 每个处理函数使用单个箭头函数(例如
    const handleClick = () => { ... }
    );避免使用返回处理函数的工厂函数。
  • 选中项存储 —— 在状态中通过ID存储选中项,从列表中推导完整项(例如
    selectedItem = items.find(i => i.id === selectedId)
    );避免列表更新时出现过时引用。

Data fetching (async: loading, error, data)

数据获取(异步:加载中、错误、数据)

  • Prefer TanStack Query — for any async call that involves
    isLoading
    , error handling, and result data, use
    useQuery
    (or
    useMutation
    for writes) instead of manual
    useState
    +
    useEffect
    . You get caching, deduplication, and consistent loading/error state for free.
  • Query key factory — define a single source of truth for cache keys (e.g.
    XxxQueryKey.all
    ,
    XxxQueryKey.list(...)
    ,
    XxxQueryKey.detail(id)
    ). See references/query-keys-example.ts and assets/hook-tanstack-query-template.ts.

  • 优先使用TanStack Query —— 对于任何涉及
    isLoading
    、错误处理和结果数据的异步调用,使用
    useQuery
    (写入操作使用
    useMutation
    )替代手动
    useState
    +
    useEffect
    。你将免费获得缓存、请求去重和一致的加载/错误状态。
  • 查询键工厂 —— 为缓存键定义单一可信源(例如
    XxxQueryKey.all
    XxxQueryKey.list(...)
    XxxQueryKey.detail(id)
    )。参见references/query-keys-example.tsassets/hook-tanstack-query-template.ts

Error Handling

错误处理

  • Context in messages — include a prefix in error and log messages (e.g.
    [ComponentName] failed to load
    ).
  • Rethrow policy — rethrow only when adding context or transforming the error type; don't rethrow after logging unless the caller needs to handle the failure.

  • 消息上下文 —— 在错误和日志消息中添加前缀(例如
    [ComponentName] 加载失败
    )。
  • 重抛策略 —— 仅在添加上下文或转换错误类型时重抛;除非调用者需要处理失败,否则日志记录后不要重抛。

Architecture & Organisation

架构与组织

  • Feature structure — each feature should be self-contained: its own components,
    hooks/
    subdirectory,
    *.utils.ts
    and
    *.types.ts
    files, and Controllers/Services for complex business logic (e.g.
    features/scene-3d/
    ,
    scene-manager/controllers/
    ).
  • Single responsibility — one clear responsibility per file; keep components small and focused. Apply the Avoiding GodComponent (decomposition strategy): utilities first, then hooks, then sub-components when the visual layer exceeds ~40 lines.
  • Composition over inheritance — prefer composing small components and utilities over class inheritance.
  • Group related code — keep related functionality together (e.g. by feature or domain).

  • 功能结构 —— 每个功能应自包含:拥有自己的组件、
    hooks/
    子目录、
    *.utils.ts
    *.types.ts
    文件,以及处理复杂业务逻辑的控制器/服务(例如
    features/scene-3d/
    scene-manager/controllers/
    )。
  • 单一职责 —— 每个文件职责明确;保持组件小巧且聚焦。应用避免上帝组件(拆分策略):先提取工具函数,再提取Hooks,当视觉层代码超过约40行时提取子组件。
  • 组合优于继承 —— 优先通过组合小型组件和工具函数实现功能,而非类继承。
  • 相关代码分组 —— 将相关功能放在一起(例如按功能或领域分组)。

Comments

注释

  • Self-documenting first — prefer clear names and structure over comments; comment only when behavior is non-obvious.
  • Explain "why" not "what" — comments should explain rationale, side effects, or workarounds, not restate the code.
  • Keep comments up to date — remove or update comments when code changes.
  • TODO with ticket ID — use a traceable format for TODOs (e.g.
    // TODO: JIRA-1234 - description
    ).

  • 优先自文档化 —— 优先使用清晰的命名和结构,而非注释;仅当行为不明显时添加注释。
  • 解释“为什么”而非“是什么” —— 注释应解释理由、副作用或变通方案,而非重复代码内容。
  • 保持注释更新 —— 代码变更时,删除或更新相关注释。
  • 带工单ID的TODO —— TODO使用可追溯格式(例如
    // TODO: JIRA-1234 - 描述
    )。

Logging

日志

  • Logger with levels — use a logger (e.g.
    logger.info()
    ,
    logger.error()
    ,
    logger.warn()
    ,
    logger.debug()
    ) instead of
    console.*
    in client code.
  • Context prefix — include a context prefix in log messages (e.g.
    [useDeviceDiscovery] storing last known camera IP
    ).
  • Server exception
    console.log
    is acceptable in server-side or API route code for debugging.

  • 分级别日志器 —— 在客户端代码中使用日志器(例如
    logger.info()
    logger.error()
    logger.warn()
    logger.debug()
    )而非
    console.*
  • 上下文前缀 —— 在日志消息中添加上下文前缀(例如
    [useDeviceDiscovery] 存储最后已知的摄像头IP
    )。
  • 服务端异常 —— 在服务端或API路由代码中,
    console.log
    可用于调试。

Function Parameters

函数参数

  • Destructuring for multiple params — use object destructuring when a function has more than one parameter (e.g.
    const fn = ({ a, b }: Args) => ...
    ).
  • Extract parameter types — export parameter types as named types/interfaces instead of inline typing.
  • Optional parameters — use
    param?: Type
    rather than
    param: Type | undefined
    .
  • Defaults in destructuring — set default values in the destructuring when possible (e.g.
    { page = 1, size = 10 }
    ).

  • 多参数使用解构 —— 当函数有多个参数时,使用对象解构(例如
    const fn = ({ a, b }: Args) => ...
    )。
  • 提取参数类型 —— 将参数类型导出为命名类型/接口,而非内联类型。
  • 可选参数 —— 使用
    param?: Type
    而非
    param: Type | undefined
  • 解构中设置默认值 —— 尽可能在解构中设置默认值(例如
    { page = 1, size = 10 }
    )。

TypeScript Best Practices

TypeScript 最佳实践

  • ReactNode for children — use
    ReactNode
    for component children (not
    JSX.Element | null | undefined
    ).
  • PropsWithChildren — use
    PropsWithChildren<Props>
    for components that accept
    children
    .
  • Record<K, V>
    — prefer the
    Record<K, V>
    utility type over custom index signatures.
  • Array.includes() — use for multiple value checks instead of repeated
    ===
    comparisons.
  • Array.some() — use for existence checks instead of
    array.find(...) !== undefined
    .
  • Explicit enum values — use explicit numeric (or string) values for enums so they survive reordering and serialization.

  • 子组件使用ReactNode —— 组件子元素类型使用
    ReactNode
    (而非
    JSX.Element | null | undefined
    )。
  • PropsWithChildren —— 接受
    children
    的组件使用
    PropsWithChildren<Props>
  • Record<K, V>
    —— 优先使用
    Record<K, V>
    工具类型,而非自定义索引签名。
  • Array.includes() —— 多值检查时使用
    Array.includes()
    ,而非重复的
    ===
    比较。
  • Array.some() —— 存在性检查时使用
    Array.some()
    ,而非
    array.find(...) !== undefined
  • 显式枚举值 —— 枚举使用显式的数值(或字符串)值,以便重新排序和序列化后仍保持有效。

React Native (when applicable)

React Native(适用时)

When working in a React Native or Expo project:
  • Spacing — prefer
    gap
    ,
    rowGap
    , and
    columnGap
    over
    margin
    /
    padding
    for spacing between elements.
  • Responsive layout — use
    useWindowDimensions
    instead of
    Dimensions.get
    for layout that reacts to size changes.
  • Static data outside components — move constants and pure functions that don't depend on props or state outside the component to avoid new references on every render.

在React Native或Expo项目中工作时:
  • 间距 —— 元素间间距优先使用
    gap
    rowGap
    columnGap
    ,而非
    margin
    /
    padding
  • 响应式布局 —— 布局需要响应尺寸变化时,使用
    useWindowDimensions
    而非
    Dimensions.get
  • 组件外的静态数据 —— 将不依赖Props或状态的常量和纯函数移至组件外部,避免每次渲染创建新引用。

Pre-output validation

输出前校验

Before returning any code, run the Pre-output Validation checklist.

返回任何代码前,运行输出前校验清单

Templates

模板

Skeletons to copy and adapt (file names in kebab-case):
TypeFileUse when
Hookassets/hook-template.tsCreating a data-fetching or effect hook
Hook (TanStack Query)assets/hook-tanstack-query-template.tsCreating a hook with @tanstack/react-query (queryKey, queryFn, placeholderData)
Componentassets/component-template.tsxCreating a React component
Utilityassets/utils-template.tsCreating pure or side-effect helpers (
*.utils.ts
)

可复制并调整的骨架文件(文件名使用短横线分隔命名):
类型文件使用场景
Hookassets/hook-template.ts创建数据获取或副作用Hook
Hook(TanStack Query)assets/hook-tanstack-query-template.ts使用@tanstack/react-query创建Hook(queryKey、queryFn、placeholderData)
组件assets/component-template.tsx创建React组件
工具函数assets/utils-template.ts创建纯函数或带副作用的工具函数(
*.utils.ts

Validation

验证

Validate this skill's frontmatter and structure with skills-ref:
bash
skills-ref validate ./skills/typescript-and-react-guidelines
使用skills-ref验证本技能的前置信息和结构:
bash
skills-ref validate ./skills/typescript-and-react-guidelines