typescript-and-react-guidelines
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCoding 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:
- Apply ALL rules to every file you create or modify
- Run the Pre-output Validation checklist before returning any code
- If a rule conflicts with a user request, flag it explicitly and propose a compliant alternative
- Reference specific rule names when explaining choices (e.g., "Per the KISS principle, I simplified this by...")
- Load example files on demand — only read the relevant file for the task at hand
阅读本技能后:
- 对所有创建或修改的文件应用全部规则
- 在返回任何代码前,运行输出前校验清单
- 如果规则与用户需求冲突,明确标记冲突并提出符合规范的替代方案
- 解释选择时引用具体规则名称(例如:“根据KISS原则,我通过...简化了这段代码”)
- 按需加载示例文件 —— 仅读取与当前任务相关的文件
Code Quality Principles
代码质量原则
| Principle | Rule |
|---|---|
| Readability First | Code is read more than written. Clear names > clever code. |
| KISS | Simplest solution that works. Avoid over-engineering. |
| Avoid GodComponent | Single responsibility per component; extract utilities, hooks, sub-components. |
| DRY | Extract common logic. Create reusable components. |
| YAGNI | Don't build features before they're needed. |
| Immutability | Never mutate — always return new values. |
| 原则 | 规则 |
|---|---|
| 可读性优先 | 代码读的次数远多于写的次数。清晰的命名优于炫技的代码。 |
| KISS | 采用最简单的可行方案。避免过度设计。 |
| 避免上帝组件 | 每个组件单一职责;提取工具函数、Hooks、子组件。 |
| DRY | 提取通用逻辑。创建可复用组件。 |
| YAGNI | 不要提前构建尚未需要的功能。 |
| 不可变性 | 绝不直接修改数据 —— 始终返回新值。 |
Avoiding GodComponent (decomposition strategy)
避免上帝组件(拆分策略)
Apply the following order to keep components focused and testable:
-
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. then
interface FormatRangeArgs { start: number; end: number }).const formatRange = ({ start, end }: FormatRangeArgs) => ... - Reusable across features → put in .
src/utils/xyz.utils.ts - Feature-specific → keep next to the component as (same kebab-case base name as the component file, e.g.
component-name.utils.tsnext tomarket-list-item.utils.ts).market-list-item.tsx
-
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 subdirectory (e.g.
hooks/).features/market-list/hooks/use-market-filters.ts
- Move state, effects, and derived logic into hooks (e.g.
-
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.
按照以下顺序操作,保持组件聚焦且可测试:
-
优先提取纯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
-
将逻辑提取为命名Hooks
- 将状态、副作用和派生逻辑移至Hooks中(例如)。
use-xyz.ts - 跨功能复用 → 放入。
src/hooks/use-xyz.ts - 特定功能专用 → 放在对应功能的子目录中(例如
hooks/)。features/market-list/hooks/use-market-filters.ts
- 将状态、副作用和派生逻辑移至Hooks中(例如
-
将视觉层拆分为子组件
- 如果渲染/JSX代码超过约40行,提取具有明确Props和单一职责的子组件。
- 每个子组件应有自己的Props接口,当代码增长时,应放在独立文件(或专用子文件夹)中。
Sections & Example Files
章节与示例文件
TypeScript / JavaScript
TypeScript / JavaScript
| Topic | Example File | When to load |
|---|---|---|
| Variable & function naming (incl. boolean prefixes) | | When naming anything (arrow functions only; booleans: is/has/should/can/will) |
| Immutability patterns | | When working with state/objects/arrays |
| Error handling | | When writing async code |
| Async / Promise patterns | | When using await/Promise |
| Type safety | | When defining interfaces/types (no inline types; no nested types; extract named types) |
| Control flow & readability | | Early returns, const vs let, Array.includes/some, nullish coalescing, destructuring |
| 主题 | 示例文件 | 加载时机 |
|---|---|---|
| 变量与函数命名(含布尔值前缀) | | 命名任何内容时(仅箭头函数;布尔值:is/has/should/can/will前缀) |
| 不可变性模式 | | 处理状态/对象/数组时 |
| 错误处理 | | 编写异步代码时 |
| 异步 / Promise 模式 | | 使用await/Promise时 |
| 类型安全 | | 定义接口/类型时(不使用内联类型;不使用嵌套类型;提取命名类型) |
| 控制流与可读性 | | 提前返回、const vs let、Array.includes/some、空值合并、解构 |
React
React
| Topic | Example File | When to load |
|---|---|---|
| Component structure | | When creating a component |
| 主题 | 示例文件 | 加载时机 |
|---|---|---|
| 组件结构 | | 创建组件时 |
Testing
测试
| Topic | Example File | When to load |
|---|---|---|
| Unit test patterns | | When writing Jest/RTL tests (AAA, screen, spyOn, it.each, getByRole, mock factory) |
| 主题 | 示例文件 | 加载时机 |
|---|---|---|
| 单元测试模式 | | 编写Jest/RTL测试时(AAA模式、screen、spyOn、it.each、getByRole、模拟工厂) |
Anti-patterns (read during code review)
反模式(代码审查时阅读)
| Topic | File |
|---|---|
| All BAD patterns grouped | |
| Code smells detection | |
| 主题 | 文件 |
|---|---|
| 汇总所有不良模式 | |
| 代码异味检测 | |
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.tsComponents and hooks are still exported with PascalCase (components) or camelCase with prefix (hooks); only the file name is kebab-case.
use- 典型文件长度 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(组件)或带前缀的camelCase(Hooks);仅文件名使用短横线分隔命名。
useCode Style / TypeScript
代码风格 / TypeScript
- TypeScript strict mode — enable in for maximum type safety.
tsconfig.json - 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 (or
FunctionComponent<Props>); use typed props interfaces, not inline orFC<Props>.any - Early returns — use early returns in component bodies to keep the main render path flat and readable.
- Fragment shorthand — use instead of
<>...</>unless a<Fragment>is required.key - Exports — prefer named exports for components; default export only when required by the framework (e.g. Expo Router).
- FunctionComponent —— 使用(或
FunctionComponent<Props>)为React组件指定类型;使用带类型的Props接口,不使用内联类型或FC<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 prefix only for real hooks; never for plain functions.
use - 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. ); avoid function factories that return handlers.
const handleClick = () => { ... } - Selected items — store selection by ID in state and derive the full item from the list (e.g. ); avoids stale references when the list updates.
selectedItem = items.find(i => i.id === selectedId)
- 函数与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 , error handling, and result data, use
isLoading(oruseQueryfor writes) instead of manualuseMutation+useState. You get caching, deduplication, and consistent loading/error state for free.useEffect - Query key factory — define a single source of truth for cache keys (e.g. ,
XxxQueryKey.all,XxxQueryKey.list(...)). See references/query-keys-example.ts and assets/hook-tanstack-query-template.ts.XxxQueryKey.detail(id)
- 优先使用TanStack Query —— 对于任何涉及、错误处理和结果数据的异步调用,使用
isLoading(写入操作使用useQuery)替代手动useMutation+useState。你将免费获得缓存、请求去重和一致的加载/错误状态。useEffect - 查询键工厂 —— 为缓存键定义单一可信源(例如、
XxxQueryKey.all、XxxQueryKey.list(...))。参见references/query-keys-example.ts和assets/hook-tanstack-query-template.ts。XxxQueryKey.detail(id)
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, subdirectory,
hooks/and*.utils.tsfiles, and Controllers/Services for complex business logic (e.g.*.types.ts,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()) instead oflogger.debug()in client code.console.* - Context prefix — include a context prefix in log messages (e.g. ).
[useDeviceDiscovery] storing last known camera IP - Server exception — is acceptable in server-side or API route code for debugging.
console.log
- 分级别日志器 —— 在客户端代码中使用日志器(例如、
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 rather than
param?: Type.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 for component children (not
ReactNode).JSX.Element | null | undefined - PropsWithChildren — use for components that accept
PropsWithChildren<Props>.children - — prefer the
Record<K, V>utility type over custom index signatures.Record<K, V> - 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, androwGapovercolumnGap/marginfor spacing between elements.padding - Responsive layout — use instead of
useWindowDimensionsfor layout that reacts to size changes.Dimensions.get - 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):
| Type | File | Use when |
|---|---|---|
| Hook | assets/hook-template.ts | Creating a data-fetching or effect hook |
| Hook (TanStack Query) | assets/hook-tanstack-query-template.ts | Creating a hook with @tanstack/react-query (queryKey, queryFn, placeholderData) |
| Component | assets/component-template.tsx | Creating a React component |
| Utility | assets/utils-template.ts | Creating pure or side-effect helpers ( |
可复制并调整的骨架文件(文件名使用短横线分隔命名):
| 类型 | 文件 | 使用场景 |
|---|---|---|
| Hook | assets/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 | 创建纯函数或带副作用的工具函数( |
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