{"id":"83017f94-d6ba-4b70-9c80-99cf1d359afb","shortId":"K5KHrS","kind":"skill","title":"typescript-rules","tagline":"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.","description":"# TypeScript Development Rules (Frontend)\n\n## Basic Principles\n\n- **Aggressive Refactoring** - Prevent technical debt and maintain health\n- **Delete code when no current caller exists** - YAGNI principle (Kent Beck)\n\n## Comment Writing Rules\n- **Function Description Focus**: Describe what the code \"does\"\n- **Timeless content only**: Record decisions and rationale; leave chronological history to version control\n- **Conciseness**: Keep explanations to necessary minimum\n\n## Type Safety\n\n**Absolute Rule**: Replace every `any` with `unknown`, generics, or union types. `any` disables type checking and causes runtime errors.\n\n**any Type Alternatives (Priority Order)**\n1. **unknown Type + Type Guards**: Use for validating external input (API responses, localStorage, URL parameters)\n2. **Generics**: When type flexibility is needed\n3. **Union Types・Intersection Types**: Combinations of multiple types\n4. **Type Assertions (Last Resort)**: Only when type is certain\n\n**Type Guard Implementation Pattern**\n```typescript\nfunction isUser(value: unknown): value is User {\n  return typeof value === 'object' && value !== null && 'id' in value && 'name' in value\n}\n```\n\n**Modern Type Features**\n- **satisfies Operator**: `const config = { apiUrl: '/api' } satisfies Config` - Preserves inference\n- **const Assertion**: `const ROUTES = { HOME: '/' } as const satisfies Routes` - Immutable and type-safe\n- **Branded Types**: `type UserId = string & { __brand: 'UserId' }` - Distinguish meaning\n- **Template Literal Types**: `type EventName = \\`on\\${Capitalize<string>}\\`` - Express string patterns with types\n\n**Type Safety in Frontend Implementation**\n- **React Props/State**: TypeScript manages types, unknown unnecessary\n- **External API Responses**: Always receive as `unknown`, validate with type guards\n- **localStorage/sessionStorage**: Treat as `unknown`, validate\n- **URL Parameters**: Treat as `unknown`, validate\n- **Form Input (Controlled Components)**: Type-safe with React synthetic events\n\n**Type Safety in Data Flow**\n- **Frontend → Backend**: Props/State (Type Guaranteed) → API Request (Serialization)\n- **Backend → Frontend**: API Response (`unknown`) → Type Guard → State (Type Guaranteed)\n\n**Type Complexity Management**\n- **Props Design**:\n  - Props count: 3-7 props ideal (consider component splitting if exceeds 10)\n  - Optional Props: 50% or less (consider default values or Context if excessive)\n  - Nesting: Up to 2 levels (flatten deeper structures)\n- Type Assertions: Review design if used 3+ times\n- **External API Types**: Relax constraints and define according to reality (convert appropriately internally)\n\n## Coding Conventions\n\n**Component Design Criteria**\n- **Function components only**: Official React recommendation, optimizable by modern tooling (Exception: Error Boundary requires class component)\n- **Custom Hooks**: Standard pattern for logic reuse and dependency injection\n- **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\n- **Co-location**: Place tests, styles, and related files alongside components\n\n**State Management Patterns**\n- **Local State**: `useState` for component-specific state\n- **Context API**: For sharing state across component tree (theme, auth, etc.)\n- **Custom Hooks**: Encapsulate state logic and side effects\n- **Server State**: React Query or SWR for API data caching\n\n**Data Flow Principles**\n- **Single Source of Truth**: Each piece of state has one authoritative source\n- **Unidirectional Flow**: Data flows top-down via props\n- **Immutable Updates**: Use immutable patterns for state updates\n\n```typescript\n// Immutable state update — always create new arrays/objects\nsetUsers(prev => [...prev, newUser])\n```\n\n**Function Design**\n- **0-2 parameters maximum**: Use object for 3+ parameters\n  ```typescript\n  function createUser({ name, email, role }: CreateUserParams) {}\n  ```\n\n**Props Design (Props-driven Approach)**\n- Props are the interface: Define all necessary information as props\n- Pass all data dependencies as props; use Context only for cross-cutting concerns (theme, auth, locale)\n- Type-safe: Always define Props type explicitly\n\n**Environment Variables**\n- **Use build tool's environment variable system**: `process.env` does not work in browsers\n- Centrally manage environment variables through configuration layer\n- Define a typed config object with defaults for every environment variable\n\n```typescript\n// Use import.meta.env (not process.env — unavailable in browser bundles)\nconst config = {\n  apiUrl: import.meta.env.API_URL || 'http://localhost:3000',\n  appName: import.meta.env.APP_NAME || 'My App'\n}\n```\n\n**Security (Client-side Constraints)**\n- **CRITICAL**: All frontend code is public and visible in browser\n- **All secrets stay server-side**: Store API keys, tokens, and secrets on the backend only\n- Exclude `.env` files via `.gitignore`\n- Limit error messages to non-sensitive context\n\n```typescript\n// Backend manages secrets, frontend accesses via proxy\nconst response = await fetch('/api/data') // Backend handles API key authentication\n```\n\n**Dependency Injection**\n- **Custom Hooks for dependency injection**: Ensure testability and modularity\n\n**Asynchronous Processing**\n- Promise Handling: Always use `async/await`\n- Error Handling: Always handle with `try-catch` or Error Boundary\n- Type Definition: Explicitly define return value types (e.g., `Promise<Result>`)\n\n**Format Rules**\n- Semicolon omission (follow Biome settings)\n- Types in `PascalCase`, variables/functions in `camelCase`\n- Imports use absolute paths (`src/`)\n\n**Clean Code Principles**\n- Delete unused code immediately\n- Delete debug `console.log()`\n- Delete commented-out code (retrieve from version control when needed)\n- Comments explain \"why\" (not \"what\")\n\n## Error Handling\n\n**Absolute Rule**: Every caught error must be logged with context and either re-thrown to Error Boundary, returned as a Result error variant, or displayed as user-facing error state.\n\n**Fail-Fast Principle**: Fail quickly on errors to prevent continued processing in invalid states\n```typescript\ncatch (error) {\n  logger.error('Processing failed', error)\n  throw error // Handle with Error Boundary or higher layer\n}\n```\n\n**Result Type Pattern**: Express errors with types for explicit handling\n```typescript\ntype Result<T, E> = { ok: true; value: T } | { ok: false; error: E }\n\n// Example: Express error possibility with types\nfunction parseUser(data: unknown): Result<User, ValidationError> {\n  if (!isValid(data)) return { ok: false, error: new ValidationError() }\n  return { ok: true, value: data as User }\n}\n```\n\n**Custom Error Classes**\n```typescript\nexport class AppError extends Error {\n  constructor(message: string, public readonly code: string, public readonly statusCode = 500) {\n    super(message)\n    this.name = this.constructor.name\n  }\n}\n// Purpose-specific: ValidationError(400), ApiError(502), NotFoundError(404)\n```\n\n**Layer-Specific Error Handling (React)**\n- Error Boundary: Catch React component errors, display fallback UI\n- Custom Hook: Detect business rule violations, propagate AppError as-is\n- API Layer: Convert fetch errors to domain errors\n\n**Structured Logging and Sensitive Information Protection**\nRedact sensitive fields (password, token, apiKey, secret, creditCard) before logging\n\n**Asynchronous Error Handling in React**\n- Error Boundary setup mandatory: Catch rendering errors\n- Use try-catch with all async/await in event handlers\n- Always log and re-throw errors or display error state\n\n## Refactoring Techniques\n\n**Basic Policy**\n- Small Steps: Maintain always-working state through gradual improvements\n- Safe Changes: Minimize the scope of changes at once\n- Behavior Guarantee: Ensure existing behavior remains unchanged while proceeding\n\n**Implementation Procedure**: Understand Current State → Gradual Changes → Behavior Verification → Final Validation\n\n**Priority**: Duplicate Code Removal > Large Function Division > Complex Conditional Branch Simplification > Type Safety Improvement\n\n## Performance Optimization\n\n- Component Memoization: Use React.memo for expensive components\n- State Optimization: Minimize re-renders with proper state structure\n- Lazy Loading: Use React.lazy and Suspense for code splitting\n- Bundle Size: Monitor with the `build` script and keep under 500KB\n\n## Non-functional Requirements\n\n- **Browser Compatibility**: Chrome/Firefox/Safari/Edge (latest 2 versions)\n- **Rendering Time**: Within 5 seconds for major pages","tags":["typescript","rules","claude","code","workflows","shinpr","agent-skills","agentic-ai","ai-agents","automation","claude-code","claude-code-plugin"],"capabilities":["skill","source-shinpr","skill-typescript-rules","topic-agent-skills","topic-agentic-ai","topic-ai-agents","topic-automation","topic-claude-code","topic-claude-code-plugin","topic-code-quality","topic-developer-tools","topic-development-workflow","topic-llm-orchestration","topic-productivity","topic-prompt-engineering"],"categories":["claude-code-workflows"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/shinpr/claude-code-workflows/typescript-rules","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add shinpr/claude-code-workflows","source_repo":"https://github.com/shinpr/claude-code-workflows","install_from":"skills.sh"}},"qualityScore":"0.613","qualityRationale":"deterministic score 0.61 from registry signals: · indexed on github topic:agent-skills · 327 github stars · SKILL.md body (8,741 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T18:53:53.655Z","embedding":null,"createdAt":"2026-04-18T22:03:21.518Z","updatedAt":"2026-05-02T18:53:53.655Z","lastSeenAt":"2026-05-02T18:53:53.655Z","tsv":"'-2':526 '-7':298 '/api':182 '/api/data':692 '0':525 '1':109 '10':306 '2':124,322,1117 '3':131,297,333,532 '3000':630 '4':140 '400':925 '404':929 '5':1122 '50':309 '500':916 '500kb':1108 '502':927 'absolut':85,751,782 'access':685 'accord':342 'across':455 'adopt':385 'aggress':34 'alongsid':437 'altern':106 'alway':237,515,577,713,718,1002,1021 'always-work':1020 'anoth':410 'api':119,235,277,282,336,451,476,658,695,956 'apierror':926 'apikey':975 'apiurl':181,626 'app':635 'apperror':903,952 'appnam':631 'approach':546 'appropri':346 'architectur':387 'arrays/objects':518 'as-i':953 'assert':142,188,328 'async/await':715,998 'asynchron':709,980 'atom':392,394 'auth':459,572 'authent':697 'authorit':492 'await':690 'backend':273,280,665,681,693 'base':405 'basic':32,1015 'beck':52 'behavior':1036,1040,1052 'biom':741 'boundari':365,726,799,841,937,986 'branch':1065 'brand':201,206 'browser':596,622,650,1113 'build':585,1103 'bundl':623,1098 'busi':948 'cach':478 'caller':47 'camelcas':748 'capit':216 'catch':723,830,938,989,995 'caught':785 'caus':101 'central':597 'certain':149 'chang':1028,1033,1051 'check':99 'chosen':419 'chrome/firefox/safari/edge':1115 'chronolog':72 'class':367,899,902 'clean':754 'client':638 'client-sid':637 'co':429 'co-loc':428 'code':24,43,62,348,644,755,759,768,911,1058,1096 'combin':136 'comment':53,766,775 'commented-out':765 'compat':1114 'complex':291,1063 'compon':11,22,259,302,350,354,368,379,386,438,447,456,940,1072,1078 'component-specif':446 'concern':570 'concis':77 'condit':1064 'config':180,184,607,625 'configur':602 'consid':301,312 'consist':415 'console.log':763 'const':179,187,189,193,624,688 'constraint':339,640 'constructor':906 'contain':407 'container-present':406 'content':65 'context':316,450,564,679,791 'continu':824 'control':76,258,772 'convent':349 'convert':345,958 'count':296 'creat':516 'createus':536 'createuserparam':540 'creditcard':977 'criteria':352 'critic':641 'cross':568 'cross-cut':567 'current':46,1048 'custom':369,461,700,897,945 'cut':569 'data':270,477,479,496,559,876,883,894 'debt':38 'debug':762 'decis':68 'deeper':325 'default':313,610 'defin':341,551,578,604,730 'definit':728 'delet':42,757,761,764 'depend':377,560,698,703 'describ':59 'descript':57 'design':12,294,330,351,393,426,524,542 'detect':947 'develop':6,29 'disabl':97 'display':807,942,1010 'distinguish':208 'divis':1062 'doc':427 'document':417 'domain':962 'driven':545 'duplic':1057 'e':859,867 'e.g':734 'effect':468 'either':793 'email':538 'encapsul':463 'ensur':705,1038 'env':668 'environ':582,588,599,613 'error':16,103,364,673,716,725,780,786,798,804,812,821,831,835,837,840,849,866,870,887,898,905,933,936,941,960,963,981,985,991,1008,1011 'etc':460 'event':266,1000 'eventnam':214 'everi':88,612,784 'exampl':868 'exceed':305 'except':363 'excess':318 'exclud':667 'exist':48,1039 'expens':1077 'explain':776 'explan':79 'explicit':581,729,853 'export':901 'express':217,848,869 'extend':904 'extern':117,234,335 'face':811 'fail':815,818,834 'fail-fast':814 'fallback':943 'fals':865,886 'fast':816 'featur':27,176,404 'feature-bas':403 'fetch':691,959 'field':972 'file':436,669 'final':1054 'flatten':324 'flexibl':128 'flow':271,480,495,497 'focus':58 'follow':412,740 'form':256 'format':736 'frontend':5,26,31,225,272,281,643,684 'function':56,155,353,523,535,874,1061,1111 'generic':92,125 'gitignor':671 'gradual':1025,1050 'guarante':276,289,1037 'guard':113,151,244,286 'handl':17,694,712,717,719,781,838,854,934,982 'handler':1001 'health':41 'hierarchi':380 'higher':843 'histori':73 'home':191 'hook':370,462,701,946 'id':168 'ideal':300 'immedi':760 'immut':196,503,506,512 'implement':20,152,226,1045 'import':749 'import.meta.env':617 'import.meta.env.api':627 'import.meta.env.app':632 'improv':1026,1069 'includ':8 'infer':186 'inform':554,968 'inject':378,699,704 'input':118,257 'interfac':550 'intern':347 'intersect':134 'invalid':827 'isus':156 'isvalid':882 'keep':78,1106 'kent':51 'key':659,696 'larg':1060 'last':143 'latest':1116 'layer':420,603,844,931,957 'layer-specif':930 'lazi':1089 'leav':71 'less':311 'level':323 'limit':672 'liter':211 'load':1090 'local':442,573 'localhost':629 'localstorag':121 'localstorage/sessionstorage':245 'locat':430 'log':789,965,979,1003 'logger.error':832 'logic':374,465 'maintain':40,1019 'major':1125 'manag':14,230,292,440,598,682 'mandatori':988 'maximum':528 'mean':209 'memoiz':1073 'messag':674,907,918 'minim':1029,1081 'minimum':82 'modern':174,361 'modular':708 'molecul':395 'monitor':1100 'multipl':138 'must':787 'name':171,537,633 'necessari':81,553 'need':130,774 'nest':319 'new':517,888 'newus':522 'non':677,1110 'non-funct':1109 'non-sensit':676 'notfounderror':928 'null':167 'object':165,530,608 'offici':356 'ok':860,864,885,891 'omiss':739 'one':491 'oper':178 'optim':1071,1080 'optimiz':359 'option':307 'order':108 'organ':396 'page':398,1126 'paramet':123,251,527,533 'parseus':875 'pascalcas':745 'pass':557 'password':973 'path':752 'pattern':153,219,372,441,507,847 'perform':1070 'piec':487 'place':431 'polici':1016 'possibl':871 'present':408 'preserv':185 'prev':520,521 'prevent':36,823 'principl':33,50,481,756,817 'prioriti':107,1056 'procedur':1046 'proceed':1044 'process':710,825,833 'process.env':591,619 'project':383,390,401,423 'promis':711,735 'prop':293,295,299,308,502,541,544,547,556,562,579 'propag':951 'proper':1086 'props-driven':543 'props/state':228,274 'protect':969 'proxi':687 'public':646,909,913 'purpos':922 'purpose-specif':921 'queri':472 'quick':819 'rational':70 're':795,1006,1083 're-rend':1082 're-throw':1005 're-thrown':794 'react':21,227,264,357,471,935,939,984 'react.lazy':1092 'react.memo':1075 'react/typescript':4 'readm':424 'readon':910,914 'realiti':344 'receiv':238 'recommend':358 'record':67 'redact':970 'refactor':35,1013 'relat':435 'relax':338 'remain':1041 'remov':1059 'render':990,1084,1119 'replac':87 'request':278 'requir':366,1112 'resort':144 'respons':120,236,283,689 'result':803,845,857,878 'retriev':769 'return':162,731,800,884,890 'reus':375 'review':329 'role':539 'rout':190,195 'rule':3,7,30,55,86,737,783,949 'runtim':102 'safe':200,262,576,1027 'safeti':10,84,223,268,1068 'satisfi':177,183,194 'scope':1031 'script':1104 'second':1123 'secret':652,662,683,976 'secur':636 'semicolon':738 'sensit':678,967,971 'serial':279 'server':469,655 'server-sid':654 'set':742 'setup':987 'setus':519 'share':453 'side':467,639,656 'simplif':1066 'singl':482 'size':1099 'skill' 'skill-typescript-rules' 'small':1017 'sourc':483,493 'source-shinpr' 'specif':448,923,932 'split':303,1097 'src':753 'standard':371 'state':13,287,439,443,449,454,464,470,489,509,513,813,828,1012,1023,1049,1079,1087 'statuscod':915 'stay':653 'step':1018 'store':657 'string':205,218,908,912 'structur':326,411,414,964,1088 'style':433 'super':917 'suspens':1094 'swr':474 'synthet':265 'system':590 'technic':37 'techniqu':1014 'templat':210,397 'test':432 'testabl':706 'theme':458,571 'this.constructor.name':920 'this.name':919 'throw':836,1007 'thrown':796 'time':334,1120 'timeless':64 'token':660,974 'tool':362,586 'top':499 'top-down':498 'topic-agent-skills' 'topic-agentic-ai' 'topic-ai-agents' 'topic-automation' 'topic-claude-code' 'topic-claude-code-plugin' 'topic-code-quality' 'topic-developer-tools' 'topic-development-workflow' 'topic-llm-orchestration' 'topic-productivity' 'topic-prompt-engineering' 'treat':246,252 'tree':457 'tri':722,994 'true':861,892 'truth':485 'try-catch':721,993 'type':9,83,95,98,105,111,112,127,133,135,139,141,147,150,175,199,202,203,212,213,221,222,231,243,261,267,275,285,288,290,327,337,575,580,606,727,733,743,846,851,856,873,1067 'type-saf':198,260,574 'typeof':163 'typescript':2,23,28,154,229,511,534,615,680,829,855,900 'typescript-rul':1 'ui':944 'unavail':620 'unchang':1042 'understand':1047 'unidirect':494 'union':94,132 'unknown':91,110,158,232,240,248,254,284,877 'unnecessari':233 'unus':758 'updat':504,510,514 'url':122,250,628 'use':18,114,332,381,391,402,505,529,563,584,616,714,750,992,1074,1091 'user':161,810,879,896 'user-fac':809 'userid':204,207 'usest':444 'valid':116,241,249,255,1055 'validationerror':880,889,924 'valu':157,159,164,166,170,173,314,732,862,893 'variabl':583,589,600,614 'variables/functions':746 'variant':805 'verif':1053 'version':75,771,1118 'via':501,670,686 'violat':950 'visibl':648 'within':1121 'work':594,1022 'write':54 'yagni':49","prices":[{"id":"a384ef0e-a233-4b7b-87f8-a90b739729c4","listingId":"83017f94-d6ba-4b70-9c80-99cf1d359afb","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"shinpr","category":"claude-code-workflows","install_from":"skills.sh"},"createdAt":"2026-04-18T22:03:21.518Z"}],"sources":[{"listingId":"83017f94-d6ba-4b70-9c80-99cf1d359afb","source":"github","sourceId":"shinpr/claude-code-workflows/typescript-rules","sourceUrl":"https://github.com/shinpr/claude-code-workflows/tree/main/skills/typescript-rules","isPrimary":false,"firstSeenAt":"2026-04-18T22:03:21.518Z","lastSeenAt":"2026-05-02T18:53:53.655Z"}],"details":{"listingId":"83017f94-d6ba-4b70-9c80-99cf1d359afb","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"shinpr","slug":"typescript-rules","github":{"repo":"shinpr/claude-code-workflows","stars":327,"topics":["agent-skills","agentic-ai","ai-agents","automation","claude-code","claude-code-plugin","code-quality","developer-tools","development-workflow","llm-orchestration","productivity","prompt-engineering","skills"],"license":"mit","html_url":"https://github.com/shinpr/claude-code-workflows","pushed_at":"2026-05-02T15:39:17Z","description":"Production-ready development workflows for Claude Code, powered by specialized AI agents.","skill_md_sha":"947ab7b73cda5205369d8d19db0d80db34799572","skill_md_path":"skills/typescript-rules/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/shinpr/claude-code-workflows/tree/main/skills/typescript-rules"},"layout":"multi","source":"github","category":"claude-code-workflows","frontmatter":{"name":"typescript-rules","description":"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."},"skills_sh_url":"https://skills.sh/shinpr/claude-code-workflows/typescript-rules"},"updatedAt":"2026-05-02T18:53:53.655Z"}}