Agent Skillsevrendom/rudel › typescript-standards

typescript-standards

GitHub

提供TypeScript编码规范,涵盖命名导出、静态导入、类型安全及命名约定。禁止类型断言,建议从Zod派生类型,确保代码质量与一致性。

.claude/skills/typescript-standards/SKILL.md evrendom/rudel

Trigger Scenarios

编写TypeScript代码 审查TypeScript代码 重构TypeScript代码

Install

npx skills add evrendom/rudel --skill typescript-standards -g -y
More Options

Non-standard path

npx skills add https://github.com/evrendom/rudel/tree/main/.claude/skills/typescript-standards -g -y

Use without installing

npx skills use evrendom/rudel@typescript-standards

指定 Agent (Claude Code)

npx skills add evrendom/rudel --skill typescript-standards -a claude-code -g -y

安装 repo 全部 skill

npx skills add evrendom/rudel --all -g -y

预览 repo 内 skill

npx skills add evrendom/rudel --list

SKILL.md

Frontmatter
{
    "name": "typescript-standards",
    "description": "TypeScript coding standards. Use when writing TypeScript, reviewing code, or refactoring. Enforces named exports, no dynamic imports, discriminated unions, proper type safety.",
    "allowed-tools": [
        "Read",
        "Edit",
        "Grep",
        "Glob"
    ]
}

TypeScript Standards

Named Exports Only

// ❌ Bad
export default function myFunction() {}

// ✅ Good
export function myFunction() {}

Package Exports (Monorepo)

Only export what other packages actually consume:

// ❌ Bad - Exports everything including internal types
export * from "./digest";

// ✅ Good - Explicit public interface
export {
  fetchCompoundDigest,
  buildCompoundDigestBlocks,
  type CompoundDigestEnv,
} from "./digest";

Before adding exports, check what's actually imported by consumer packages.

Import Patterns

Prefer Static Imports

Always use static imports at the top of the file. Only use dynamic imports (await import()) when:

  • Conditional loading is required (feature flags, environment checks)
  • Code splitting is intentional for performance
  • Circular dependency breaking is necessary

Never use dynamic imports just to import something mid-function when a static import would work.

Type-Only Imports

For values used only as types, use explicit type-only imports:

// ❌ Bad - Imports value but only uses type
import { MyClass } from './my-class'
type Instance = MyClass

// ✅ Good - Clear intent, no runtime import
import { type MyClass } from './my-class'
type Instance = MyClass

Naming Conventions

  • Files: kebab-case (my-component.ts)
  • Variables/functions: camelCase (myVariable, myFunction())
  • Classes/types/interfaces: PascalCase (MyClass, MyInterface)
  • Constants/enum values: ALL_CAPS (MAX_COUNT)
  • Generic type parameters: Prefix with T (TKey, TValue)

Type Safety Rules

Derive Types from Zod Schemas

When a Zod schema exists, always derive the TypeScript type from it using z.infer. Never define a separate interface that duplicates the schema's shape:

// ❌ Bad - Manually duplicates the Zod schema, drifts over time
export interface User {
  id: string
  name: string
  email: string
}

// ✅ Good - Single source of truth
import { type User } from "@rudel/api-routes"

// ✅ Good - Extend when the service needs extra internal fields
import { type User as UserBase } from "@rudel/api-routes"
export interface User extends UserBase {
  session_count: number
}

If the type needs fields beyond the schema, extend the inferred type rather than redefining it.

No Type Casts

Never use as or as unknown as type assertions. They hide type incompatibilities:

// ❌ Bad - Hides the real problem
const env = context.env as unknown as ExecutorEnv

// ✅ Good - Fix the underlying type mismatch
// Option 1: Properly type the source
const env: ExecutorEnv = context.env

// Option 2: Use type guards for runtime checking
function isExecutorEnv(env: unknown): env is ExecutorEnv {
  return typeof env === 'object' && env !== null && 'DB' in env
}

If you encounter code that seems to require a cast:

  1. Investigate why the types don't match
  2. Fix the source type definitions
  3. If copying from existing code with casts, note it needs cleanup

Prefer Discriminated Unions

// ✅ Good - Prevents impossible states
type FetchingState<TData> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: TData }
  | { status: 'error'; error: Error }

// Handle with switch
switch (state.status) {
  case 'success':
    console.log(state.data)
    break
}

Optional Properties

Use sparingly. Prefer T | undefined over T? to force explicit passing:

// ✅ Good
type AuthOptions = {
  userId: string | undefined
}

Prefer Interfaces for Extending

// ❌ Bad - & operator has terrible performance
type C = A & B

// ✅ Good
interface C extends A, B {}

Don't Use Enums

Use as const objects instead:

const sizes = {
  xs: 'EXTRA_SMALL',
  sm: 'SMALL',
} as const

type Size = keyof typeof sizes

Installing Libraries

Never rely on training data for versions. Always install latest:

pnpm add -D @typescript-eslint/eslint-plugin

Async Operations

Use Promise.all for parallel operations:

const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()])

Immutability

  • Use readonly arrays
  • Use const assertions
  • Create new objects instead of mutating

Version History

  • 781a16e Current 2026-07-24 11:32

Same Skill Collection

.claude/skills/api-testing/SKILL.md
.claude/skills/clickhouse-architecture-advisor/SKILL.md
.claude/skills/clickhouse-best-practices/SKILL.md
.claude/skills/clickhouse-js-node-coding/SKILL.md
.claude/skills/clickhouse-query/SKILL.md
.claude/skills/code-architecture/SKILL.md
.claude/skills/environment-variables/SKILL.md
.claude/skills/library-docs/SKILL.md
.claude/skills/pr-creation/SKILL.md
.claude/skills/testing-bun/SKILL.md
.claude/skills/clickhouse-js-node-troubleshooting/SKILL.md

Metadata

Files
0
Version
59fc2ed
Hash
71410376
Indexed
2026-07-24 11:32

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-07 17:18
浙ICP备14020137号-1 $Carte des visiteurs$