Typescript rules
Skill shinpr/claude-code-workflows/dev-workflows-frontend/skills/typescript-rules
Production-ready development workflows for Claude Code, powered by specialized AI agents.
npx -y skills add shinpr/claude-code-workflows --skill typescript-rulesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
React/TypeScript frontend development rules including type safety, component design, state management, and error handling. Use when implementing React components, TypeScript code, or frontend features.
SKILL.md
13.3 KB, as published. Nobody here has run it
TypeScript Development Rules (Frontend)
Basic Principles
- Aggressive Refactoring - Prevent technical debt and maintain health
- Delete code when no current caller exists - YAGNI principle (Kent Beck)
Comment Writing Rules
Code first: names and types carry meaning; a comment must add what code cannot, and one comment per decision is enough. Frontend specifics:
- Comment intent, not markup: explain why a component memoizes, guards, or re-renders — not what the JSX renders
- Timeless content only: Record decisions and rationale; leave chronological history to version control
Type Safety
Default Rule: Prefer unknown, generics, or union types over any. Retain any only when an existing external, generated, or legacy public signature requires it, or when replacing it prevents the project type check from expressing a safe generic relationship. Record the declaration path or type-check result that proves the constraint. When a local adapter can preserve compatibility and expose a safer type within the user request or current task/design artifact, implement the adapter; otherwise confine any to the smallest adapter or public-signature boundary, document the reason, and validate untrusted data before it enters typed application code.
any Type Alternatives (Priority Order)
- unknown Type + Type Guards: Use for validating external input (API responses, localStorage, URL parameters)
- Generics: When type flexibility is needed
- Union Types・Intersection Types: Combinations of multiple types
- Type Assertions (Last Resort): Only when type is certain
Type Guard Implementation Pattern
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'id' in value && 'name' in value
}
Modern Type Features
- satisfies Operator:
const config = { apiUrl: '/api' } satisfies Config- Preserves inference - const Assertion:
const ROUTES = { HOME: '/' } as const satisfies Routes- Immutable and type-safe - Branded Types:
type UserId = string & { __brand: 'UserId' }- Distinguish meaning - Template Literal Types:
type EventName = \on${Capitalize<string>}`` - Express string patterns with types
Type Safety in Frontend Implementation
- React Props/State: TypeScript manages types, unknown unnecessary
- External API Responses: Treat unvalidated payloads as
unknownand validate at the boundary. A generated client may retain its declared type when it also enforces the contract at runtime - localStorage/sessionStorage: Handle
string | nullat the browser boundary; treat parsed data asunknownuntil validated - URL Parameters: Handle the router or platform's
string | nullshape, then parse and validate before converting to a domain type - Form Input (Controlled Components): Type-safe with React synthetic events
Type Safety in Data Flow
- Frontend → Backend: Props/State (Type Guaranteed) → API Request (Serialization)
- Backend → Frontend: API Response (
unknown) → Type Guard → State (Type Guaranteed)
Type Complexity Review Signals
Use the following as prompts to review a design, not as pass/fail thresholds. Existing project conventions and a component's actual responsibility take precedence.
- Props Design:
- Props count: 3-7 props ideal (consider component splitting if exceeds 10)
- Optional Props: 50% or less (consider default values or Context if excessive)
- Nesting: Up to 2 levels (flatten deeper structures)
- Type Assertions: Review design if used 3+ times
- External API Types: Relax constraints and define according to reality (convert appropriately internally)
Coding Conventions
Component Design Criteria
- Function components for new code: Prefer function components and Hooks. Preserve working class components unless the user request or current task/design artifact requires migration; a class component remains valid when implementing an Error Boundary directly
- Custom Hooks: Standard pattern for logic reuse and dependency injection
- Component Hierarchy: Use the project's adopted component architecture. When the project uses Atomic Design: Atoms → Molecules → Organisms → Templates → Pages. When the project uses Feature-based, Container-Presenter, or another structure: follow that structure consistently and document the chosen layering in the project README or design doc
- File placement: Follow the repository's existing component, test, and style layout. Co-locate related files only when that is the established convention or an approved new structure
Server/Client Boundary (RSC frameworks only — e.g., Next.js App Router)
- Default to server components for data fetching and rendering; isolate interactivity behind a
"use client"boundary at the smallest scope that needs it - Keep browser-only APIs (
window,localStorage, event handlers) inside client components; calling them in a server component breaks the render - N/A for client-only SPAs (e.g., Vite) — skip when the project has no server-component runtime
State Management Patterns
- Local State:
useStatefor component-specific state - Context API: For sharing state across component tree (theme, auth, etc.)
- Custom Hooks: Encapsulate state logic and side effects
- Server State: Follow the project's existing server-state layer; React Query and SWR are common options when already adopted
Data Flow Principles
- Single Source of Truth: Each piece of state has one authoritative source
- Unidirectional Flow: Data flows top-down via props
- Immutable Updates: Use immutable patterns for state updates
// Immutable state update — always create new arrays/objects
setUsers(prev => [...prev, newUser])
Function Design
- Prefer 0-2 parameters. For 3+ related values, use an object when it clarifies names or represents one domain input; preserve a positional signature when the repository convention or external API requires it
function createUser({ name, email, role }: CreateUserParams) {}
Props Design (Props-driven Approach)
- Props are the interface: Define all necessary information as props
- Declare data dependencies through props, hooks, Context, or injected modules according to the project's established state and dependency boundaries
- Type-safe: Always define Props type explicitly
Environment Variables
- Use the build tool's env accessor: read client-side env through the bundler's exposed accessor — Vite via
import.meta.env, Next.js/CRA via prefixedprocess.env. Raw, unprefixed access isundefinedin the browser bundle - Only prefixed vars reach the client: build tools expose only vars carrying their public prefix; an unprefixed var is
undefinedin the browser. The prefix differs per tool — match the project's bundler (ViteVITE_, Next.js publicNEXT_PUBLIC_, CRAREACT_APP_) - Centrally validate env through a typed config layer. Fail fast for missing required values; provide defaults only for optional values or an explicitly defined local-development mode
// Vite example. Use the project's existing schema validator when one is configured.
const apiUrl = import.meta.env.VITE_API_URL
if (!apiUrl) {
throw new Error('Missing required client environment variable: VITE_API_URL')
}
const config = {
apiUrl,
appName: import.meta.env.VITE_APP_NAME ?? 'My App' // optional: intentional product default
}
Security (Client-side Constraints)
- CRITICAL: All frontend code is public and visible in browser
- All secrets stay server-side: Store API keys, tokens, and secrets on the backend only
- Exclude
.envfiles via.gitignore - Limit error messages to non-sensitive context
// Backend manages secrets, frontend accesses via proxy
const response = await fetch('/api/data') // Backend handles API key authentication
Dependency Injection
- Custom Hooks for dependency injection: Ensure testability and modularity
Asynchronous Processing
- Promise Handling: Follow the repository's established style; prefer
async/awaitwhen it makes sequencing and error propagation clearer - Error Handling: Handle event-handler and asynchronous failures with
try-catch, a typedResult, or user-visible error state. Error Boundaries cover rendering failures in descendant components, not event handlers or ordinary asynchronous callbacks - Type Definition: Explicitly define return types at exported APIs and important boundaries (e.g.,
Promise<Result>); allow inference for local implementations when the contract remains clear - Effect race/cleanup: guard
useEffectdata fetches against out-of-order responses and post-unmount state updates — abort or ignore stale results (AbortControlleror a mounted flag), or use a server-state library (React Query/SWR) that cancels and dedupes.try-catchalone does not cover this
Format Rules
- Semicolon usage follows the project's formatter settings
- Types in
PascalCase, variables/functions incamelCase - Imports follow the repository's existing TypeScript, bundler, and package-boundary configuration. Use aliases only when the project config resolves them; do not invent a
src/alias
Clean Code Principles
- Delete unused code immediately
- Delete debug
console.log() - Delete commented-out code (retrieve from version control when needed)
- Comments explain "why" (not "what")
Error Handling
Handling Rule: Every caught error must be intentionally propagated, converted to a typed error result, or represented as user-facing error state. Log it once at the boundary that owns diagnosis or recovery, with sensitive data redacted; avoid duplicate logging while propagating the same failure.
Fail-Fast Principle: Fail quickly on errors to prevent continued processing in invalid states
catch (error) {
logger.error('Processing failed', error)
throw error // Handle with Error Boundary or higher layer
}
Result Type Pattern: Express errors with types for explicit handling
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }
// Example: Express error possibility with types
function parseUser(data: unknown): Result<User, ValidationError> {
if (!isValid(data)) return { ok: false, error: new ValidationError() }
return { ok: true, value: data as User }
}
Custom Error Classes
export class AppError extends Error {
constructor(message: string, public readonly code: string, public readonly statusCode = 500) {
super(message)
this.name = this.constructor.name
}
}
// Purpose-specific: ValidationError(400), ApiError(502), NotFoundError(404)
Layer-Specific Error Handling (React)
- Error Boundary: Place at meaningful UI recovery boundaries to catch descendant rendering errors and display fallback UI
- Custom Hook: Detect business rule violations, propagate AppError as-is
- API Layer: Convert fetch errors to domain errors
Structured Logging and Sensitive Information Protection Redact sensitive fields (password, token, apiKey, secret, creditCard) before logging
Asynchronous Error Handling in React
- Add an Error Boundary where a rendering failure needs localized recovery; use the application's top-level boundary when localized recovery adds no value
- Handle expected failures in event handlers and async workflows with
try-catch, typed results, or rejected-promise propagation to the owning layer - Preserve the failure context and choose one observable outcome: propagate it, return an error variant, or display error state
Refactoring Techniques
Basic Policy
- Small Steps: Maintain always-working state through gradual improvements
- Safe Changes: Minimize the scope of changes at once
- Behavior Guarantee: Ensure existing behavior remains unchanged while proceeding
Implementation Procedure: Understand Current State → Gradual Changes → Behavior Verification → Final Validation
Priority: Duplicate Code Removal > Large Function Division > Complex Conditional Branch Simplification > Type Safety Improvement
Performance Optimization
- Automatic memoization: when React Compiler is enabled, rely on it; reach for manual
React.memo/useMemo/useCallbackonly as a profiler- or identity-justified escape hatch (a measured bottleneck, or stable reference identity for third-party APIs / effect dependencies) - State Optimization: Minimize re-renders with proper state structure
- Lazy Loading: Use
React.lazyandSuspensefor code splitting - Bundle Size: Monitor via the build script against the project's budget
Non-functional Requirements
- Browser Compatibility: Implement against the support policy in the PRD, Design Doc, Browserslist, or build configuration. When none is defined, preserve the repository's current transpilation/polyfill baseline and surface any new browser-dependent API as an unresolved compatibility decision
- Performance: Verify against project-defined budgets and the metric that represents the affected experience (for example Web Vitals, interaction latency, bundle size, or a profiler trace). When no budget exists, measure the changed path and report the observed result instead of inventing a threshold
Gives 0 of the 12 instructions most design frontend skills give
Counted across 1,170 of the 1,878 authors here whose files we hold, read 2026-08-06
- use css variables for color consistencyin 73 of 1170, across 24 files
- match implementation complexity to the aesthetic visionin 70 of 1170, across 20 files
- commit to one bold aesthetic direction before codingin 70 of 1170, across 25 files
- add atmospheric background effects and texturesin 58 of 1170, across 10 files
- use unexpected spatial compositions and layoutsin 55 of 1170, across 7 files
- implement real working codein 55 of 1170, across 7 files
- vary themes and aesthetics across different designsin 48 of 1170, across 7 files
- launch chromium in headless modein 47 of 1170, across 4 files
- close the browser when donein 47 of 1170, across 4 files
- run provided scripts with help flag firstin 47 of 1170, across 4 files
- use descriptive selectors for elementsin 47 of 1170, across 4 files
- wait for network idle statein 46 of 1170, across 3 files
Said here and by no other author read
- Manage environment variables through a single typed configuration layer
- Use unidirectional and immutable state update patterns
- Delete unused code and debug logs immediately
- Prefer unknown generics or unions over the any type
- Validate untrusted external data at boundaries using type guards
- Isolate browser-only APIs inside client components
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.