enforcing-typescript-standards
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEnforcing TypeScript Standards
强制执行TypeScript标准
Enforces the project's core TypeScript standards including explicit typing, import organization, class member ordering, and code safety rules.
强制执行项目的核心TypeScript标准,包括显式类型定义、导入组织、类成员排序和代码安全规则。
Triggers
触发场景
Activate this skill when the user says or implies any of these:
- "write", "create", "implement", "add", "build" (new TypeScript code)
- "fix", "update", "change", "modify", "refactor" (existing TypeScript code)
- "review", "check", "improve", "clean up" (code quality)
- Any request involving or
.tsfiles.tsx
Specific triggers:
- Creating a new or
.tsfile.tsx - Modifying existing TypeScript code
- Reviewing TypeScript code for compliance
当用户提及或暗示以下内容时,启用此规范:
- "编写"、"创建"、"实现"、"添加"、"构建"(新的TypeScript代码)
- "修复"、"更新"、"更改"、"修改"、"重构"(现有TypeScript代码)
- "审查"、"检查"、"改进"、"清理"(代码质量)
- 任何涉及.ts或.tsx文件的请求
具体触发场景:
- 创建新的.ts或.tsx文件
- 修改现有TypeScript代码
- 审查TypeScript代码是否符合规范
Core Standards
核心标准
Type Safety
类型安全
- Explicit return types: Prefer explicit return types when practical; omit when inference is obvious and adds no clarity
- Explicit member accessibility: Class members require ,
public, orprivateprotected - Type-only imports: Use for types:
import typeimport type { Foo } from './foo.js' - Sorted type constituents: Union/intersection types must be alphabetically sorted
- Only throw Error objects: Never throw strings or other primitives
- Avoid and type assertions: Prefer proper typing over
anyoranycasts; use them only when truly necessaryas - Type JSON fields explicitly: Use or specific interfaces for JSON data, never
Record<string, unknown>any - Use Number() for conversion: Prefer over
Number(value)orparseInt(value, 10)parseFloat(value) - Reuse existing types: Before defining a new interface, search for existing types that can be reused directly, extended, or derived using ,
Pick,Omit, or other utility typesPartial
- 显式返回类型:在实际可行时优先使用显式返回类型;当类型推断明显且不会降低可读性时可省略
- 显式成员访问修饰符:类成员必须指定、
public或privateprotected - 仅类型导入:使用导入类型:
import typeimport type { Foo } from './foo.js' - 排序类型组成:联合/交叉类型必须按字母顺序排序
- 仅抛出Error对象:永远不要抛出字符串或其他原始类型
- 避免和类型断言:优先使用正确的类型定义,而非
any或any类型转换;仅在绝对必要时使用它们as - 显式类型化JSON字段:对JSON数据使用或特定接口,绝不要使用
Record<string, unknown>any - 使用Number()进行转换:优先使用而非
Number(value)或parseInt(value, 10)parseFloat(value) - 复用现有类型:在定义新接口之前,搜索可直接复用、扩展或通过、
Pick、Omit等工具类型派生的现有类型Partial
Alternatives to Type Assertions
类型断言的替代方案
Before using , try these approaches in order:
as- Proper typing at the source
- Type guards (,
typeof)instanceof - Type narrowing through control flow
- Custom type predicate functions
- Discriminated unions
ts
// Bad
const user = data as User;
// Good
function isUser(data: unknown): data is User {
return typeof data === 'object' && data !== null && 'id' in data;
}
if (isUser(data)) {
// data is now typed as User
}在使用之前,按以下顺序尝试这些方法:
as- 在数据源处进行正确的类型定义
- 类型守卫(、
typeof)instanceof - 通过控制流缩小类型范围
- 自定义类型谓词函数
- 可辨识联合类型
ts
// 不良示例
const user = data as User;
// 良好示例
function isUser(data: unknown): data is User {
return typeof data === 'object' && data !== null && 'id' in data;
}
if (isUser(data)) {
// 此时data的类型为User
}Import Organization
导入组织
- Import order: builtin → external → internal → parent → sibling → index (alphabetized within groups)
- No duplicate imports: Consolidate imports from the same module
- Newline after imports: Blank line required after import block
- 导入顺序:内置模块 → 外部模块 → 内部模块 → 父级模块 → 同级模块 → 索引模块(每组内按字母顺序排序)
- 无重复导入:合并来自同一模块的导入
- 导入后换行:导入块后必须留空行
Class Member Ordering
类成员排序
- Signatures (call/construct)
- Fields: private → public → protected
- Constructors: public → protected → private
- Methods: public → protected → private
- 签名(调用/构造函数)
- 字段:private → public → protected
- 构造函数:public → protected → private
- 方法:public → protected → private
Code Style
代码风格
- Simplicity over cleverness: Straightforward, readable code is better than clever one-liners
- Early returns: Use guard clauses to reduce nesting; return early for edge cases
- Nullish coalescing: Prefer over
??for defaults (avoids false positives on||or0)'' - Optional chaining: Use for safe property access
?. - Match existing patterns: Follow conventions already established in the codebase
- Meaningful identifiers: Names must be descriptive (exceptions: ,
_,i,j,k,e,x)y - Function declarations: Use not
function foo()const foo = function() - Prefer const: Use unless reassignment is needed
const - No var: Always use or
constlet - Object shorthand: Use not
{ foo }{ foo: foo } - Template literals: Use not
`Hello ${name}`'Hello ' + name - Strict equality: Use except for null comparisons
=== - One class per file: Maximum one class definition per file
- Avoid : Prefer
reduceloops or other array methods for clarityfor...of - Functions over classes: Prefer exported functions over classes with static methods (unless state is needed)
- No nested functions: Define helper functions at module level, not inside other functions
- Immutability: Create new objects/arrays instead of mutating existing ones
- 简洁胜于技巧:直接、可读的代码优于巧妙的单行代码
- 提前返回:使用守卫语句减少嵌套;对边缘情况提前返回
- 空值合并运算符:优先使用而非
??设置默认值(避免将||或0误判为假值)'' - 可选链运算符:使用进行安全的属性访问
?. - 匹配现有模式:遵循代码库中已确立的约定
- 有意义的标识符:名称必须具有描述性(例外:、
_、i、j、k、e、x)y - 函数声明:使用而非
function foo()const foo = function() - 优先使用const:除非需要重新赋值,否则使用
const - 禁止使用var:始终使用或
constlet - 对象简写:使用而非
{ foo }{ foo: foo } - 模板字符串:使用而非
`Hello ${name}`'Hello ' + name - 严格相等:除了null比较外,使用
=== - 每个文件一个类:每个文件最多定义一个类
- 避免使用reduce:为了清晰,优先使用循环或其他数组方法
for...of - 优先使用函数而非类:优先使用导出函数而非仅含静态方法的类(除非需要维护状态)
- 禁止嵌套函数:在模块级别定义辅助函数,而非在其他函数内部
- 不可变性:创建新的对象/数组,而非修改现有对象/数组
Naming Conventions
命名约定
- Enum members: Use (e.g.,
PascalCase)MyValue - No trailing underscores: Identifiers cannot end with
_
- 枚举成员:使用(例如:
PascalCase)MyValue - 禁止尾随下划线:标识符不能以下划线结尾
_
Comments
注释
- No redundant comments: Never comment what the code already expresses clearly
- No duplicate comments: Don't repeat information from function names, types, or nearby comments
- Meaningful only: Only add comments to explain why, not what — the code shows what it does
- 无冗余注释:永远不要注释代码已清晰表达的内容
- 无重复注释:不要重复函数名、类型或附近注释中的信息
- 仅添加有意义的注释:仅添加注释解释_为什么_,而非_做什么_——代码本身会展示它的功能
Boolean Expressions
布尔表达式
- Prefer truthiness checks: Use implicit truthy/falsy checks over explicit comparisons
- Exception: Use explicit checks when distinguishing /
0(valid values) from''/nullis semantically importantundefined
- 优先使用真值检查:使用隐式真值/假值检查而非显式比较
- 例外情况:当在语义上需要区分/
0(有效值)与''/null时,使用显式检查undefined
Testing
测试
- Minimize mocking: Avoid mocking everything; use real implementations and data generators when available
- Test real behavior: Testing mocks provides little value — test actual code paths
- Don't be lazy: Write thorough tests that cover edge cases, not just happy paths
- 最小化模拟:避免模拟所有内容;在可用时使用真实实现和数据生成器
- 测试真实行为:模拟测试几乎没有价值——测试实际的代码路径
- 不要偷懒:编写全面的测试,覆盖边缘情况,而不仅仅是正常路径
Error Handling
错误处理
- Specific error types: Prefer specific error types over generic when meaningful
Error - Avoid silent failures: Don't swallow errors with empty catch blocks
- Handle rejections: Always handle promise rejections
- Let errors propagate: Don't catch errors just to re-throw or log — let them bubble up to error handlers
- 特定错误类型:在有意义时优先使用特定错误类型而非通用
Error - 避免静默失败:不要用空的catch块吞噬错误
- 处理拒绝:始终处理Promise的拒绝
- 让错误传播:不要为了重新抛出或记录而捕获错误——让它们冒泡到错误处理程序
Negative Knowledge
反模式规避
Avoid these anti-patterns:
- statements in production code
console.log() - or
eval()constructorFunction() - Nested ternary operators
- inside loops when
awaitwould be simpler (sequential awaits are fine when order matters or parallelism adds complexity)Promise.all - Empty interfaces
- Variable shadowing
- Functions defined inside loops
- without explanation (use
@ts-ignorewith 10+ char description)@ts-expect-error - Comments that restate the code: above
// increment countercounter++ - Comments that duplicate type information: when return type is
// returns a string: string - Commented-out code (delete it; use version control)
- Verbose boolean comparisons: ,
arr.length > 0,str !== ''obj !== null && obj !== undefined - Disabling lint rules via comments (fix the code instead)
- Overuse of type or
anytype assertionsas - Over-mocking in tests instead of using real implementations or data generators
- Empty catch blocks that silently swallow errors
- Using for defaults when
||is more appropriate?? - Deep nesting when early returns would simplify
- Catching errors just to re-throw or log them
- Nested function definitions inside other functions
- Mutating objects/arrays instead of creating new ones
- TOCTOU: Checking file/resource existence before operating (try and handle errors instead)
- Classes with only static methods (use plain functions instead)
- Duplicating existing interfaces instead of reusing or deriving with /
Pick/OmitPartial
避免以下不良实践:
- 生产代码中的语句
console.log() - 或
eval()构造函数Function() - 嵌套三元运算符
- 当更简单时在循环内使用
Promise.all(当顺序重要或并行处理增加复杂度时,顺序await是可以的)await - 空接口
- 变量遮蔽
- 在循环内定义函数
- 无解释的(使用带有10个字符以上描述的
@ts-ignore)@ts-expect-error - 重复代码的注释:放在
// increment counter上方counter++ - 重复类型信息的注释:当返回类型为时,不要写
: string// returns a string - 被注释掉的代码(删除它;使用版本控制)
- 冗长的布尔比较:、
arr.length > 0、str !== ''obj !== null && obj !== undefined - 通过注释禁用lint规则(修复代码代替)
- 过度使用类型或
any类型断言as - 测试中过度模拟而非使用真实实现或数据生成器
- 吞噬错误的空catch块
- 当更合适时使用
??设置默认值|| - 当提前返回可以简化时使用深层嵌套
- 仅为了重新抛出或记录而捕获错误
- 在其他函数内部定义嵌套函数
- 修改现有对象/数组而非创建新对象/数组
- TOCTOU:在操作前检查文件/资源是否存在(尝试操作并处理错误代替)
- 仅含静态方法的类(改用普通函数)
- 重复现有接口而非复用或通过/
Pick/Omit派生Partial
Verification Workflow
验证流程
- Analyze: Compare the code change against these TypeScript standards
- Generate/Refactor: Write or modify code to comply with all rules above
- Simplify: Review for opportunities to simplify — prefer clear, straightforward code over clever solutions
- Review naming: Verify variable and function names still make sense in context after changes
- Build: Verify types compile without errors (e.g., or
npm run build)npx tsc --noEmit - Lint: Run to confirm compliance before completing the task
npm run lint
- 分析:将代码变更与这些TypeScript标准进行比较
- 生成/重构:编写或修改代码以符合上述所有规则
- 简化:审查简化的机会——优先选择清晰、直接的代码而非巧妙的解决方案
- 审查命名:验证变量和函数名在变更后的上下文中仍然有意义
- 构建:验证类型编译无错误(例如:或
npm run build)npx tsc --noEmit - Lint检查:在完成任务前运行确认符合规范
npm run lint
Examples
示例
Comments Examples
注释示例
ts
// Standard
// Retry with exponential backoff to handle transient network failures
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
for (let i = 0; i < attempts; i++) {
try {
return await fetch(url);
} catch {
await sleep(2 ** i * 100);
}
}
throw new Error(`Failed after ${attempts} attempts`);
}
// Non-Standard
/**
* Fetches data from a URL with retry logic
* @param url - The URL to fetch from
* @param attempts - Number of attempts (default 3)
* @returns A Promise that resolves to a Response
*/
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
// Loop through attempts
for (let i = 0; i < attempts; i++) {
try {
// Try to fetch the URL
return await fetch(url);
} catch {
// Wait before retrying
await sleep(2 ** i * 100);
}
}
// Throw error if all attempts fail
throw new Error(`Failed after ${attempts} attempts`);
}ts
// 标准示例
// 使用指数退避重试以处理临时网络故障
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
for (let i = 0; i < attempts; i++) {
try {
return await fetch(url);
} catch {
await sleep(2 ** i * 100);
}
}
throw new Error(`Failed after ${attempts} attempts`);
}
// 非标准示例
/**
* 带重试逻辑的URL数据获取
* @param url - 要获取的URL
* @param attempts - 尝试次数(默认3次)
* @returns 解析为Response的Promise
*/
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
// 循环尝试
for (let i = 0; i < attempts; i++) {
try {
// 尝试获取URL
return await fetch(url);
} catch {
// 重试前等待
await sleep(2 ** i * 100);
}
}
// 所有尝试失败时抛出错误
throw new Error(`Failed after ${attempts} attempts`);
}Boolean Expressions Examples
布尔表达式示例
ts
// Standard
if (myArray.length) {
}
if (myString) {
}
if (myObject) {
}
if (!value) {
}
// Non-Standard
if (myArray.length !== 0) {
}
if (myArray.length > 0) {
}
if (myString !== '') {
}
if (myObject !== null && myObject !== undefined) {
}
if (value === null || value === undefined) {
}ts
// 标准示例
if (myArray.length) {
}
if (myString) {
}
if (myObject) {
}
if (!value) {
}
// 非标准示例
if (myArray.length !== 0) {
}
if (myArray.length > 0) {
}
if (myString !== '') {
}
if (myObject !== null && myObject !== undefined) {
}
if (value === null || value === undefined) {
}Early Return Examples
提前返回示例
ts
// Standard
function processUser(user: User | null): Result {
if (!user) {
return { error: 'No user provided' };
}
if (!user.isActive) {
return { error: 'User is inactive' };
}
return { data: transform(user) };
}
// Non-Standard
function processUser(user: User | null): Result {
if (user) {
if (user.isActive) {
return { data: transform(user) };
} else {
return { error: 'User is inactive' };
}
} else {
return { error: 'No user provided' };
}
}ts
// 标准示例
function processUser(user: User | null): Result {
if (!user) {
return { error: '未提供用户' };
}
if (!user.isActive) {
return { error: '用户已失效' };
}
return { data: transform(user) };
}
// 非标准示例
function processUser(user: User | null): Result {
if (user) {
if (user.isActive) {
return { data: transform(user) };
} else {
return { error: '用户已失效' };
}
} else {
return { error: '未提供用户' };
}
}Functions Over Classes Examples
优先使用函数而非类示例
ts
// Standard
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
export function formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// Non-Standard
export class Calculator {
static calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
static formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
}ts
// 标准示例
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
export function formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// 非标准示例
export class Calculator {
static calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
static formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
}No Nested Functions Examples
禁止嵌套函数示例
ts
// Standard
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
async function processItems(items: Item[]): Promise<TransformedItem[]> {
return items.map(transformItem);
}
// Non-Standard
async function processItems(items: Item[]): Promise<TransformedItem[]> {
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
return items.map(transformItem);
}ts
// 标准示例
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
async function processItems(items: Item[]): Promise<TransformedItem[]> {
return items.map(transformItem);
}
// 非标准示例
async function processItems(items: Item[]): Promise<TransformedItem[]> {
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
return items.map(transformItem);
}Immutability Examples
不可变性示例
ts
// Standard
function addItem(items: Item[], newItem: Item): Item[] {
return [...items, newItem];
}
function removeItem(items: Item[], id: string): Item[] {
return items.filter((item) => item.id !== id);
}
function updateItem(items: Item[], id: string, updates: Partial<Item>): Item[] {
return items.map((item) => (item.id === id ? { ...item, ...updates } : item));
}
// Non-Standard
function addItem(items: Item[], newItem: Item): Item[] {
items.push(newItem);
return items;
}
function removeItem(items: Item[], id: string): Item[] {
const index = items.findIndex((item) => item.id === id);
items.splice(index, 1);
return items;
}ts
// 标准示例
function addItem(items: Item[], newItem: Item): Item[] {
return [...items, newItem];
}
function removeItem(items: Item[], id: string): Item[] {
return items.filter((item) => item.id !== id);
}
function updateItem(items: Item[], id: string, updates: Partial<Item>): Item[] {
return items.map((item) => (item.id === id ? { ...item, ...updates } : item));
}
// 非标准示例
function addItem(items: Item[], newItem: Item): Item[] {
items.push(newItem);
return items;
}
function removeItem(items: Item[], id: string): Item[] {
const index = items.findIndex((item) => item.id === id);
items.splice(index, 1);
return items;
}Error Propagation Examples
错误传播示例
ts
// Standard
async function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// Non-Standard
async function getUser(id: string): Promise<User> {
try {
return await userService.findById(id);
} catch (error) {
console.error(error);
throw error;
}
}ts
// 标准示例
async function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// 非标准示例
async function getUser(id: string): Promise<User> {
try {
return await userService.findById(id);
} catch (error) {
console.error(error);
throw error;
}
}TOCTOU Examples
TOCTOU示例
ts
// Standard
async function readConfig(path: string): Promise<Config> {
try {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
} catch (error) {
if (isNotFoundError(error)) {
return defaultConfig;
}
throw error;
}
}
// Non-Standard
async function readConfig(path: string): Promise<Config> {
if (await fileExists(path)) {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
}
return defaultConfig;
}ts
// 标准示例
async function readConfig(path: string): Promise<Config> {
try {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
} catch (error) {
if (isNotFoundError(error)) {
return defaultConfig;
}
throw error;
}
}
// 非标准示例
async function readConfig(path: string): Promise<Config> {
if (await fileExists(path)) {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
}
return defaultConfig;
}Type Reuse Examples
类型复用示例
ts
// Given an existing type
interface User {
id: string;
email: string;
name: string;
passwordHash: string;
createdAt: Date;
updatedAt: Date;
}
// Standard - derive from existing type
type PublicUser = Omit<User, 'passwordHash'>;
type UserSummary = Pick<User, 'id' | 'name'>;
type UserUpdate = Partial<Pick<User, 'email' | 'name'>>;
// Non-Standard - duplicating fields that already exist
interface PublicUser {
id: string;
email: string;
name: string;
createdAt: Date;
updatedAt: Date;
}
interface UserSummary {
id: string;
name: string;
}
interface UserUpdate {
email?: string;
name?: string;
}ts
// 给定现有类型
interface User {
id: string;
email: string;
name: string;
passwordHash: string;
createdAt: Date;
updatedAt: Date;
}
// 标准示例 - 从现有类型派生
type PublicUser = Omit<User, 'passwordHash'>;
type UserSummary = Pick<User, 'id' | 'name'>;
type UserUpdate = Partial<Pick<User, 'email' | 'name'>>;
// 非标准示例 - 重复已存在的字段
interface PublicUser {
id: string;
email: string;
name: string;
createdAt: Date;
updatedAt: Date;
}
interface UserSummary {
id: string;
name: string;
}
interface UserUpdate {
email?: string;
name?: string;
}