[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71HGPD6N9JH2G8EVBXJR77X":3,"skill-same:01M71HGPD6N9JH2G8EVBXJR77X":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":50,"install":51,"resources":64},"01M71HGPD6N9JH2G8EVBXJR77X","typescript-patterns",null,"TypeScript best practices and patterns for type-safe, maintainable code. Covers discriminated unions, generics, const assertions, type narrowing, and common anti-patterns. Extends (does not override) project tsconfig. EN triggers: \u002Ftypescript, TypeScript patterns, TS best practices, type safety, discriminated unions, generics, type narrowing. PT triggers: \u002Ftypescript, padrões TypeScript, boas práticas TS, segurança de tipos, unions discriminadas, genéricos. Do NOT use for: JavaScript-only projects, runtime performance, or replacing project tsconfig rules.","runecraftai\u002Farcanum","runecraftai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F279098826?v=4","01M71HBGS1RCAJ490RAYST7M00","01M71HBGT1CB94MNS6T6SGAZKW",1,"CC-BY-4.0","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Ftypescript-patterns\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"patterns","typescript patterns","typescript","common","code","best practices","Rules","assertions","use","only","triggers","maintainable code","en","performance","project","runtime","pt","for","projects","do","2026-11-10 10:12:30",false,"---\nname: typescript-patterns\ndescription: >\n  TypeScript best practices and patterns for type-safe, maintainable code.\n  Covers discriminated unions, generics, const assertions, type narrowing,\n  and common anti-patterns. Extends (does not override) project tsconfig.\n  EN triggers: \u002Ftypescript, TypeScript patterns, TS best practices, type safety, discriminated unions, generics, type narrowing.\n  PT triggers: \u002Ftypescript, padrões TypeScript, boas práticas TS, segurança de tipos, unions discriminadas, genéricos.\n  Do NOT use for: JavaScript-only projects, runtime performance, or replacing project tsconfig rules.\nlicense: CC-BY-4.0\n---\n\n# TypeScript Patterns\n\n## Overview\n\nThis skill provides TypeScript patterns that produce type-safe, maintainable code. It extends your project's tsconfig — not overrides it. When in conflict, the project tsconfig wins.\n\nAll patterns assume strict mode is enabled (`strict: true` in tsconfig) and that `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, and other strict-family flags are on when available. The patterns work with the strictest reasonable settings — they never rely on lax compiler options.\n\n## When to Use\n\n- Writing new TypeScript code\n- Refactoring existing code for stronger type safety\n- Reviewing code for type-level issues\n- Eliminating `any` and `as` casts from a codebase\n- Designing shared types, APIs, or generic utilities\n- Deciding between structurally equivalent alternatives (union vs enum, optional vs `| undefined`)\n\n## Core Patterns\n\n### 1. Discriminated Unions\n\nUse a `kind` (or `type`) field to create a union that the compiler can exhaustively narrow.\n\n```typescript\ntype Shape =\n  | { kind: 'circle'; radius: number }\n  | { kind: 'rectangle'; width: number; height: number }\n  | { kind: 'triangle'; base: number; height: number };\n\nfunction area(shape: Shape): number {\n  switch (shape.kind) {\n    case 'circle':\n      return Math.PI * shape.radius ** 2;\n    case 'rectangle':\n      return shape.width * shape.height;\n    case 'triangle':\n      return (shape.base * shape.height) \u002F 2;\n  }\n}\n```\n\n**Exhaustiveness check** — add a `never`-asserting helper so adding a new variant produces a compile error, not a silent fallthrough:\n\n```typescript\nfunction assertNever(value: never): never {\n  throw new Error(`Unhandled case: ${JSON.stringify(value)}`);\n}\n\nfunction area(shape: Shape): number {\n  switch (shape.kind) {\n    case 'circle':\n      return Math.PI * shape.radius ** 2;\n    case 'rectangle':\n      return shape.width * shape.height;\n    case 'triangle':\n      return (shape.base * shape.height) \u002F 2;\n    default:\n      return assertNever(shape);\n  }\n}\n```\n\nPrefer string literal unions over boolean flags for mutually exclusive states. `{ status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error }` is clearer than `{ loading: boolean; data?: T; error?: Error }` and eliminates impossible states.\n\n### 2. Generics with Constraints\n\nAlways constrain generic parameters to the shape you actually depend on. A bare `\u003CT>` gives no information to the caller or the compiler.\n\n```typescript\n\u002F\u002F BAD: T could be anything — no way to access .id\nfunction first\u003CT>(items: T[]): T {\n  return items[0];\n}\n\n\u002F\u002F GOOD: constraint communicates intent and enables property access\nfunction first\u003CT extends { id: string }>(items: T[]): T {\n  return items[0];\n}\n```\n\nKeep generics shallow. If you need four or more type parameters, reconsider the abstraction — it may be too general or the responsibilities should be split.\n\n```typescript\n\u002F\u002F BAD: too many unconstrained params\nfunction map\u003CS, T, U, V>(a: S[], b: T[], fn: (a: S, b: T) => [U, V]): [U[], V[]] { \u002F* ... *\u002F }\n\n\u002F\u002F GOOD: compose simpler functions instead\n```\n\nUse `extends` to narrow, `infer` to extract (in conditional types), and defaults (`\u003CT = unknown>`) to make call sites ergonomic.\n\n### 3. Const Assertions\n\n`as const` tells the compiler to infer the narrowest possible type — literal values and `readonly` tuples\u002Fobjects.\n\n```typescript\n\u002F\u002F Without as const: widened to string[]\nconst COLORS = ['red', 'green', 'blue'];\n\u002F\u002F type: string[]\n\n\u002F\u002F With as const: narrowed to readonly tuple of literals\nconst COLORS = ['red', 'green', 'blue'] as const;\n\u002F\u002F type: readonly [\"red\", \"green\", \"blue\"]\n\ntype Color = (typeof COLORS)[number];\n\u002F\u002F type Color = \"red\" | \"green\" | \"blue\"\n```\n\nUse `as const` for:\n- Enumerations (prefer over `enum`)\n- Configuration objects that drive conditional types\n- Literal-tuple derived union types\n- `const` objects used as lookup tables\n\n```typescript\nconst STATUS_MAP = {\n  draft: 'Draft',\n  published: 'Published',\n  archived: 'Archived',\n} as const;\n\ntype Status = keyof typeof STATUS_MAP; \u002F\u002F \"draft\" | \"published\" | \"archived\"\n```\n\n### 4. Type Narrowing\n\nNarrow types explicitly. The compiler can follow `typeof`, `instanceof`, `in`, and custom type guards — use them instead of casting.\n\n```typescript\nfunction process(input: string | number | Date) {\n  if (typeof input === 'string') {\n    return input.toUpperCase();       \u002F\u002F input: string\n  }\n  if (typeof input === 'number') {\n    return input.toFixed(2);          \u002F\u002F input: number\n  }\n  return input.toISOString();         \u002F\u002F input: Date (by elimination)\n}\n```\n\n**Custom type guards** make complex narrowing reusable:\n\n```typescript\nfunction isUser(obj: unknown): obj is User {\n  return (\n    typeof obj === 'object' &&\n    obj !== null &&\n    'id' in obj &&\n    'email' in obj\n  );\n}\n\nfunction process(input: unknown) {\n  if (isUser(input)) {\n    console.log(input.email); \u002F\u002F input: User\n  }\n}\n```\n\n**`in` operator** for discriminated properties when you don't control the union:\n\n```typescript\ntype Response = SuccessResponse | ErrorResponse;\n\nif ('data' in response) {\n  console.log(response.data); \u002F\u002F response: SuccessResponse\n}\n```\n\nUse `unknown` at system boundaries (API responses, parsed JSON, message queues). Narrow to a known type before use — never cast.\n\n```typescript\nfunction parseApiResponse(raw: unknown): User[] {\n  if (!Array.isArray(raw)) throw new Error('Expected array');\n  return raw.map((item) => {\n    if (!isUser(item)) throw new Error('Invalid user shape');\n    return item; \u002F\u002F item: User\n  });\n}\n```\n\n### 5. Template Literal Types\n\nUse template literal types for string pattern matching — route params, event names, CSS-in-JS, and more.\n\n```typescript\ntype EventName = `${string}:${string}`;\n\u002F\u002F \"click:button\", \"key:enter\", \"submit:form\"\n\ntype CSSUnit = `${number}${'px' | 'rem' | 'em' | '%'}`;\n\u002F\u002F \"16px\", \"1.5rem\", \"80%\"\n\ntype RouteParams\u003CT extends string> =\n  T extends `${string}:${infer Param}\u002F${infer Rest}`\n    ? Param | RouteParams\u003CRest>\n    : T extends `${string}:${infer Param}`\n      ? Param\n      : never;\n\ntype Params = RouteParams\u003C'\u002Fusers\u002F:userId\u002Fposts\u002F:postId'>;\n\u002F\u002F \"userId\" | \"postId\"\n```\n\nCombine with `Capitalize`, `Uncapitalize`, `Uppercase`, and `Lowercase` intrinsic types for string transformation:\n\n```typescript\ntype Getters\u003CT extends string> = `get${Capitalize\u003CT>}`;\ntype UserGetters = Getters\u003C'name' | 'email'>; \u002F\u002F \"getName\" | \"getEmail\"\n```\n\n### 6. Utility Types\n\nKnow the standard library before writing custom mapped types.\n\n| Utility | Use when |\n|---------|----------|\n| `Partial\u003CT>` | All properties optional — e.g., update DTOs |\n| `Required\u003CT>` | All properties required — remove optionality |\n| `Pick\u003CT, K>` | Select a subset of keys — API response shaping |\n| `Omit\u003CT, K>` | Exclude specific keys — public vs internal types |\n| `Record\u003CK, V>` | Map a union of keys to a single value type |\n| `Readonly\u003CT>` | Prevent mutation — function parameters, config |\n| `NonNullable\u003CT>` | Strip `null` and `undefined` from a union |\n| `ReturnType\u003CT>` | Extract the return type of a function type |\n| `Awaited\u003CT>` | Unwrap a Promise type recursively |\n\n```typescript\ninterface User {\n  id: string;\n  email: string;\n  passwordHash: string;\n  createdAt: Date;\n}\n\ntype PublicUser = Omit\u003CUser, 'passwordHash'>;\ntype UpdateUserDTO = Partial\u003CPick\u003CUser, 'email'>>;\n\ntype Status = 'active' | 'inactive';\nconst statusLabels: Record\u003CStatus, string> = {\n  active: 'Active',\n  inactive: 'Inactive',\n};\n```\n\n## Anti-Patterns to Avoid\n\n### `any` — Use `unknown` Then Narrow\n\n`any` disables type checking entirely. Use `unknown` at boundaries and narrow before use.\n\n```typescript\n\u002F\u002F BAD\nfunction handle(data: any) {\n  console.log(data.email); \u002F\u002F no error, no safety\n}\n\n\u002F\u002F GOOD\nfunction handle(data: unknown) {\n  if (isUser(data)) {\n    console.log(data.email); \u002F\u002F safe\n  }\n}\n```\n\n### Type Assertions (`as Type`) — Prefer Type Guards\n\nAssertions silence the compiler but don't validate at runtime. A type guard narrows through actual checks.\n\n```typescript\n\u002F\u002F BAD: assertion — no runtime check\nconst user = response.data as User;\n\n\u002F\u002F GOOD: type guard — validates at runtime\nif (!isUser(response.data)) throw new Error('Unexpected shape');\nconst user = response.data; \u002F\u002F user: User\n```\n\nThe one acceptable use of `as` is `as const` for literal inference (see Core Pattern 3).\n\n### Overly Complex Generics\n\nIf a generic has four or more type parameters, or if the conditional types are three levels deep, it's probably over-designed. Prefer simpler, more concrete types unless the abstraction pays for itself across at least three call sites.\n\n### `enum` — Prefer `as const` Objects or String Literal Unions\n\nTypeScript's `enum` is a runtime construct with surprising behavior (reverse mapping, numeric auto-increment, const-enum pitfalls). String literal unions and `as const` objects are simpler and more predictable.\n\n```typescript\n\u002F\u002F BAD: enum\nenum Status {\n  Active = 'active',\n  Inactive = 'inactive',\n}\n\n\u002F\u002F GOOD: string literal union\ntype Status = 'active' | 'inactive';\n\n\u002F\u002F GOOD: as const object (when you need runtime access to values)\nconst Status = {\n  Active: 'active',\n  Inactive: 'inactive',\n} as const;\ntype Status = (typeof Status)[keyof typeof Status];\n```\n\n### Optional Chaining Abuse\n\n`?.` everywhere makes error sources invisible. Use it intentionally at boundaries where `null`\u002F`undefined` is expected, and prefer early returns with explicit null checks inside function bodies.\n\n```typescript\n\u002F\u002F BAD: silent failure — if anything is missing, result is undefined with no trace\nconst result = data?.nested?.deeply?.value?.toString()?.toUpperCase();\n\n\u002F\u002F GOOD: explicit null checks with clear error boundaries\nfunction format(data: Data): string {\n  if (!data.nested?.deeply?.value) {\n    throw new Error('Missing deeply nested value');\n  }\n  return data.nested.deeply.value.toString().toUpperCase();\n}\n```\n\n## Verification\n\nAfter applying TypeScript patterns to new or refactored code:\n\n- [ ] Zero `any` in new code (or every `any` is justified by a comment explaining why `unknown`+narrowing wasn't feasible)\n- [ ] Type assertions (`as Type`) are replaced by type guards or explicit validation\n- [ ] Discriminated unions have an exhaustiveness check (`assertNever` in `default` branch)\n- [ ] Generics have meaningful constraints (`extends ...`) — no bare `\u003CT>`\n- [ ] `enum` declarations are avoided in favor of string unions or `as const` objects\n- [ ] `unknown` is used at system boundaries (API responses, parsed JSON, message queues) and narrowed before use\n- [ ] Optional chaining is used intentionally at boundaries, not as a blanket pattern throughout functions\n- [ ] The code compiles with `strict: true` and no `@ts-ignore` or `@ts-expect-error` suppression comments\n","# TypeScript Patterns\n\n## Overview\n\nThis skill provides TypeScript patterns that produce type-safe, maintainable code. It extends your project's tsconfig — not overrides it. When in conflict, the project tsconfig wins.\n\nAll patterns assume strict mode is enabled (`strict: true` in tsconfig) and that `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, and other strict-family flags are on when available. The patterns work with the strictest reasonable settings — they never rely on lax compiler options.\n\n## When to Use\n\n- Writing new TypeScript code\n- Refactoring existing code for stronger type safety\n- Reviewing code for type-level issues\n- Eliminating `any` and `as` casts from a codebase\n- Designing shared types, APIs, or generic utilities\n- Deciding between structurally equivalent alternatives (union vs enum, optional vs `| undefined`)\n\n## Core Patterns\n\n### 1. Discriminated Unions\n\nUse a `kind` (or `type`) field to create a union that the compiler can exhaustively narrow.\n\n```typescript\ntype Shape =\n  | { kind: 'circle'; radius: number }\n  | { kind: 'rectangle'; width: number; height: number }\n  | { kind: 'triangle'; base: number; height: number };\n\nfunction area(shape: Shape): number {\n  switch (shape.kind) {\n    case 'circle':\n      return Math.PI * shape.radius ** 2;\n    case 'rectangle':\n      return shape.width * shape.height;\n    case 'triangle':\n      return (shape.base * shape.height) \u002F 2;\n  }\n}\n```\n\n**Exhaustiveness check** — add a `never`-asserting helper so adding a new variant produces a compile error, not a silent fallthrough:\n\n```typescript\nfunction assertNever(value: never): never {\n  throw new Error(`Unhandled case: ${JSON.stringify(value)}`);\n}\n\nfunction area(shape: Shape): number {\n  switch (shape.kind) {\n    case 'circle':\n      return Math.PI * shape.radius ** 2;\n    case 'rectangle':\n      return shape.width * shape.height;\n    case 'triangle':\n      return (shape.base * shape.height) \u002F 2;\n    default:\n      return assertNever(shape);\n  }\n}\n```\n\nPrefer string literal unions over boolean flags for mutually exclusive states. `{ status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error }` is clearer than `{ loading: boolean; data?: T; error?: Error }` and eliminates impossible states.\n\n### 2. Generics with Constraints\n\nAlways constrain generic parameters to the shape you actually depend on. A bare `\u003CT>` gives no information to the caller or the compiler.\n\n```typescript\n\u002F\u002F BAD: T could be anything — no way to access .id\nfunction first\u003CT>(items: T[]): T {\n  return items[0];\n}\n\n\u002F\u002F GOOD: constraint communicates intent and enables property access\nfunction first\u003CT extends { id: string }>(items: T[]): T {\n  return items[0];\n}\n```\n\nKeep generics shallow. If you need four or more type parameters, reconsider the abstraction — it may be too general or the responsibilities should be split.\n\n```typescript\n\u002F\u002F BAD: too many unconstrained params\nfunction map\u003CS, T, U, V>(a: S[], b: T[], fn: (a: S, b: T) => [U, V]): [U[], V[]] { \u002F* ... *\u002F }\n\n\u002F\u002F GOOD: compose simpler functions instead\n```\n\nUse `extends` to narrow, `infer` to extract (in conditional types), and defaults (`\u003CT = unknown>`) to make call sites ergonomic.\n\n### 3. Const Assertions\n\n`as const` tells the compiler to infer the narrowest possible type — literal values and `readonly` tuples\u002Fobjects.\n\n```typescript\n\u002F\u002F Without as const: widened to string[]\nconst COLORS = ['red', 'green', 'blue'];\n\u002F\u002F type: string[]\n\n\u002F\u002F With as const: narrowed to readonly tuple of literals\nconst COLORS = ['red', 'green', 'blue'] as const;\n\u002F\u002F type: readonly [\"red\", \"green\", \"blue\"]\n\ntype Color = (typeof COLORS)[number];\n\u002F\u002F type Color = \"red\" | \"green\" | \"blue\"\n```\n\nUse `as const` for:\n- Enumerations (prefer over `enum`)\n- Configuration objects that drive conditional types\n- Literal-tuple derived union types\n- `const` objects used as lookup tables\n\n```typescript\nconst STATUS_MAP = {\n  draft: 'Draft',\n  published: 'Published',\n  archived: 'Archived',\n} as const;\n\ntype Status = keyof typeof STATUS_MAP; \u002F\u002F \"draft\" | \"published\" | \"archived\"\n```\n\n### 4. Type Narrowing\n\nNarrow types explicitly. The compiler can follow `typeof`, `instanceof`, `in`, and custom type guards — use them instead of casting.\n\n```typescript\nfunction process(input: string | number | Date) {\n  if (typeof input === 'string') {\n    return input.toUpperCase();       \u002F\u002F input: string\n  }\n  if (typeof input === 'number') {\n    return input.toFixed(2);          \u002F\u002F input: number\n  }\n  return input.toISOString();         \u002F\u002F input: Date (by elimination)\n}\n```\n\n**Custom type guards** make complex narrowing reusable:\n\n```typescript\nfunction isUser(obj: unknown): obj is User {\n  return (\n    typeof obj === 'object' &&\n    obj !== null &&\n    'id' in obj &&\n    'email' in obj\n  );\n}\n\nfunction process(input: unknown) {\n  if (isUser(input)) {\n    console.log(input.email); \u002F\u002F input: User\n  }\n}\n```\n\n**`in` operator** for discriminated properties when you don't control the union:\n\n```typescript\ntype Response = SuccessResponse | ErrorResponse;\n\nif ('data' in response) {\n  console.log(response.data); \u002F\u002F response: SuccessResponse\n}\n```\n\nUse `unknown` at system boundaries (API responses, parsed JSON, message queues). Narrow to a known type before use — never cast.\n\n```typescript\nfunction parseApiResponse(raw: unknown): User[] {\n  if (!Array.isArray(raw)) throw new Error('Expected array');\n  return raw.map((item) => {\n    if (!isUser(item)) throw new Error('Invalid user shape');\n    return item; \u002F\u002F item: User\n  });\n}\n```\n\n### 5. Template Literal Types\n\nUse template literal types for string pattern matching — route params, event names, CSS-in-JS, and more.\n\n```typescript\ntype EventName = `${string}:${string}`;\n\u002F\u002F \"click:button\", \"key:enter\", \"submit:form\"\n\ntype CSSUnit = `${number}${'px' | 'rem' | 'em' | '%'}`;\n\u002F\u002F \"16px\", \"1.5rem\", \"80%\"\n\ntype RouteParams\u003CT extends string> =\n  T extends `${string}:${infer Param}\u002F${infer Rest}`\n    ? Param | RouteParams\u003CRest>\n    : T extends `${string}:${infer Param}`\n      ? Param\n      : never;\n\ntype Params = RouteParams\u003C'\u002Fusers\u002F:userId\u002Fposts\u002F:postId'>;\n\u002F\u002F \"userId\" | \"postId\"\n```\n\nCombine with `Capitalize`, `Uncapitalize`, `Uppercase`, and `Lowercase` intrinsic types for string transformation:\n\n```typescript\ntype Getters\u003CT extends string> = `get${Capitalize\u003CT>}`;\ntype UserGetters = Getters\u003C'name' | 'email'>; \u002F\u002F \"getName\" | \"getEmail\"\n```\n\n### 6. Utility Types\n\nKnow the standard library before writing custom mapped types.\n\n| Utility | Use when |\n|---------|----------|\n| `Partial\u003CT>` | All properties optional — e.g., update DTOs |\n| `Required\u003CT>` | All properties required — remove optionality |\n| `Pick\u003CT, K>` | Select a subset of keys — API response shaping |\n| `Omit\u003CT, K>` | Exclude specific keys — public vs internal types |\n| `Record\u003CK, V>` | Map a union of keys to a single value type |\n| `Readonly\u003CT>` | Prevent mutation — function parameters, config |\n| `NonNullable\u003CT>` | Strip `null` and `undefined` from a union |\n| `ReturnType\u003CT>` | Extract the return type of a function type |\n| `Awaited\u003CT>` | Unwrap a Promise type recursively |\n\n```typescript\ninterface User {\n  id: string;\n  email: string;\n  passwordHash: string;\n  createdAt: Date;\n}\n\ntype PublicUser = Omit\u003CUser, 'passwordHash'>;\ntype UpdateUserDTO = Partial\u003CPick\u003CUser, 'email'>>;\n\ntype Status = 'active' | 'inactive';\nconst statusLabels: Record\u003CStatus, string> = {\n  active: 'Active',\n  inactive: 'Inactive',\n};\n```\n\n## Anti-Patterns to Avoid\n\n### `any` — Use `unknown` Then Narrow\n\n`any` disables type checking entirely. Use `unknown` at boundaries and narrow before use.\n\n```typescript\n\u002F\u002F BAD\nfunction handle(data: any) {\n  console.log(data.email); \u002F\u002F no error, no safety\n}\n\n\u002F\u002F GOOD\nfunction handle(data: unknown) {\n  if (isUser(data)) {\n    console.log(data.email); \u002F\u002F safe\n  }\n}\n```\n\n### Type Assertions (`as Type`) — Prefer Type Guards\n\nAssertions silence the compiler but don't validate at runtime. A type guard narrows through actual checks.\n\n```typescript\n\u002F\u002F BAD: assertion — no runtime check\nconst user = response.data as User;\n\n\u002F\u002F GOOD: type guard — validates at runtime\nif (!isUser(response.data)) throw new Error('Unexpected shape');\nconst user = response.data; \u002F\u002F user: User\n```\n\nThe one acceptable use of `as` is `as const` for literal inference (see Core Pattern 3).\n\n### Overly Complex Generics\n\nIf a generic has four or more type parameters, or if the conditional types are three levels deep, it's probably over-designed. Prefer simpler, more concrete types unless the abstraction pays for itself across at least three call sites.\n\n### `enum` — Prefer `as const` Objects or String Literal Unions\n\nTypeScript's `enum` is a runtime construct with surprising behavior (reverse mapping, numeric auto-increment, const-enum pitfalls). String literal unions and `as const` objects are simpler and more predictable.\n\n```typescript\n\u002F\u002F BAD: enum\nenum Status {\n  Active = 'active',\n  Inactive = 'inactive',\n}\n\n\u002F\u002F GOOD: string literal union\ntype Status = 'active' | 'inactive';\n\n\u002F\u002F GOOD: as const object (when you need runtime access to values)\nconst Status = {\n  Active: 'active',\n  Inactive: 'inactive',\n} as const;\ntype Status = (typeof Status)[keyof typeof Status];\n```\n\n### Optional Chaining Abuse\n\n`?.` everywhere makes error sources invisible. Use it intentionally at boundaries where `null`\u002F`undefined` is expected, and prefer early returns with explicit null checks inside function bodies.\n\n```typescript\n\u002F\u002F BAD: silent failure — if anything is missing, result is undefined with no trace\nconst result = data?.nested?.deeply?.value?.toString()?.toUpperCase();\n\n\u002F\u002F GOOD: explicit null checks with clear error boundaries\nfunction format(data: Data): string {\n  if (!data.nested?.deeply?.value) {\n    throw new Error('Missing deeply nested value');\n  }\n  return data.nested.deeply.value.toString().toUpperCase();\n}\n```\n\n## Verification\n\nAfter applying TypeScript patterns to new or refactored code:\n\n- [ ] Zero `any` in new code (or every `any` is justified by a comment explaining why `unknown`+narrowing wasn't feasible)\n- [ ] Type assertions (`as Type`) are replaced by type guards or explicit validation\n- [ ] Discriminated unions have an exhaustiveness check (`assertNever` in `default` branch)\n- [ ] Generics have meaningful constraints (`extends ...`) — no bare `\u003CT>`\n- [ ] `enum` declarations are avoided in favor of string unions or `as const` objects\n- [ ] `unknown` is used at system boundaries (API responses, parsed JSON, message queues) and narrowed before use\n- [ ] Optional chaining is used intentionally at boundaries, not as a blanket pattern throughout functions\n- [ ] The code compiles with `strict: true` and no `@ts-ignore` or `@ts-expect-error` suppression comments\n",{"name":5,"license":14,"description":43},"TypeScript best practices and patterns for type-safe, maintainable code. Covers discriminated unions, generics, const assertions, type narrowing, and common anti-patterns. Extends (does not override) project tsconfig. EN triggers: \u002Ftypescript, TypeScript patterns, TS best practices, type safety, discriminated unions, generics, type narrowing. PT triggers: \u002Ftypescript, padrões TypeScript, boas práticas TS, segurança de tipos, unions discriminadas, genéricos. Do NOT use for: JavaScript-only projects, runtime performance, or replacing project tsconfig rules.\n","main","33b040feb49e126600394272a8e321a3cd279e8b","packages\u002Fspells\u002Fskills\u002Ftypescript-patterns",0,"https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum","https:\u002F\u002Fgithub.com\u002Frunecraftai","2026-07-29 22:08:52",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add runecraftai\u002Farcanum --skill typescript-patterns",{"tool":57,"command":58},"bunx","bunx skills add runecraftai\u002Farcanum --skill typescript-patterns",{"tool":60,"command":61},"pnpm","pnpm dlx skills add runecraftai\u002Farcanum --skill typescript-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Ftypescript-patterns\u002FSKILL.md",true,[65],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},"README.md","REFERENCE","参考文档",610,"https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Ftypescript-patterns\u002FREADME.md",[72,102,128,149,173,196],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":83,"publishedAt":38},"01M71HGTN9FRQ3WEZMVVX7C3VZ","using-agent-skills","Discovers and invokes the right @runecraft\u002Fspells skill for the current task. Use when starting a session, when a request is ambiguous about which workflow to apply, or when you need to pick among the 13-skill Arcanum catalog. EN triggers: \u002Fskill, which skill, pick a skill, what skill should I use, skill discovery, meta skill. PT triggers: qual skill, qual habilidade, descobrir skill, qual fluxo usar. Do NOT use for: actual implementation work (route to the specific skill instead), unrelated standalone tasks outside the Arcanum catalog, or runtime-specific configuration (e.g., opencode.json \u002F Claude Code plugin paths).","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Fusing-agent-skills\u002FSKILL.md",[78],{"categoryNo":79,"categoryName":80,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[84,85,86,34,87,88,89,90,91,22,92,93,94,95,96,97,98,99,100,101],"using agent skills","standalone","which","pick","Meta","when","workflow","you","tasks","skill discovery","route","work","unrelated","opencode","request","paths","a skill","ambiguous",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":114,"publishedAt":38},"01M71HGJAKX4TFN0PECYPJ9S38","test-driven-development","Drives development with tests — fail first, then make them pass, then refactor. Use when implementing any logic, fixing any bug, or changing any behavior. EN triggers: \u002Ftest, write tests first, TDD, prove it, 80\u002F15\u002F5 pyramid, Beyonce Rule. PT triggers: \u002Fteste, testes primeiro, provar com teste, pirâmide 80\u002F15\u002F5. Do NOT use for: production incident triage (use \u002Fdebug), or cosmetic-only changes with no behavioral surface.","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Ftest-driven-development\u002FSKILL.md",[108,111],{"categoryNo":109,"categoryName":110,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":112,"categoryName":113,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000008","测试与质量",[115,116,117,118,34,119,120,121,27,89,122,123,124,125,126,37,35,30,28,127],"test driven development","development","test","driven","incident","implementing","write tests","write","bug","refactor","behavioral","make","tests",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":134,"publishedAt":38},"01M71HGC1H4JQFJWH613XTJC59","spec-driven","Feature planning and implementation with 4 adaptive phases — Specify, Design, Tasks, Execute. Auto-sizes depth by complexity. Creates atomic tasks with verification criteria, atomic git commits, and requirement traceability. Features an independent Verifier (author != verifier, evidence-or-zero), persistent decision log (STATE.md), and test-coverage-matrix-driven tests, plus a self-improving lessons layer that turns verification failures into reusable project-local guidance. Stack-agnostic. Use when planning features (requirements, design, task breakdown), implementing with verification and atomic commits, or validating\u002Fverifying an implementation against a spec. Triggers (PT\u002FEN): \"specify feature\", \"vamos especificar\", \"discutir feature\", \"discutir este caso\", \"design\", \"design da feature\", \"tarefas\", \"quebrar em tarefas\", \"implementar\", \"build\", \"validar\", \"verify work\", \"UAT\", \"validar implementação\", \"record decision\", \"pause work\", \"pausar trabalho\", \"resume work\", \"retomar trabalho\". Do NOT use for architecture decomposition analysis (use architecture skills) or technical design docs (treat as Complex scope: discuss gray areas + architecture-focused design).","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Fspec-driven\u002FSKILL.md",[],[135,136,118,137,138,139,140,34,32,141,120,142,143,144,145,146,89,147,92,148],"spec driven","spec","coverage","complexity","task breakdown","scope","decision","local","design","self-improving","auto","atomic commits","analysis","architecture",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":159,"publishedAt":38},"01M71HG5CSP04F29HRAG7XVYKG","skill-forge","Designs, authors, validates, and optimizes new Agent Skills from scratch. Use when creating a new skill, packaging a workflow as a skill, improving a skill's triggering accuracy, or evaluating whether a skill improves output quality. Covers the full lifecycle: DISCOVER, DESIGN, AUTHOR, VALIDATE, OPTIMIZE, DELIVER. Produces skills that follow the open SKILL.md format (YAML frontmatter + Markdown body, progressive disclosure, validation loops). EN triggers: \u002Fforge, create a skill, write a skill, build a skill, new skill, author a skill, package this as a skill, validate a skill, improve skill triggering, optimize skill description, evaluate skill quality. PT triggers: \u002Fforge, criar uma skill, escrever uma skill, construir uma skill, nova skill, empacotar como skill, validar skill, melhorar disparo, otimizar descrição de skill. Do NOT use for: discovering which existing skill to apply (use a dispatch\u002Fmeta skill), planning feature implementation, or designing software architecture.","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Fskill-forge\u002FSKILL.md",[155,158],{"categoryNo":156,"categoryName":157,"taxonomyType":81,"taxonomyTypeDesc":82},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":79,"categoryName":80,"taxonomyType":81,"taxonomyTypeDesc":82},[160,161,162,86,163,164,34,165,88,166,167,143,148,168,169,122,170,100,171,172],"forge","skill forge","deliver","new agent","feature implementation","packaging","improve","open","create","de","software","new skill","full",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":179,"publishedAt":38},"01M71HG1RAQY3GXX0Q5TMW0XD6","shipping-and-launch","Prepares production launches with a pre-launch checklist, staged rollout, monitoring, and a rollback strategy — plus feature flag lifecycle (off → internal → beta → GA). Use when deploying a feature to production for the first time, releasing a significant change, or planning a rollout. EN triggers: \u002Fship, pre-launch checklist, staged rollout, feature flag, rollback plan, monitoring. PT triggers: \u002Fshippar, checklist pré-lançamento, rollout gradual, feature flag, plano de rollback. Do NOT use for: pre-production development work, or one-off internal tools with no user exposure.","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Fshipping-and-launch\u002FSKILL.md",[],[180,181,182,34,183,184,89,185,186,187,188,95,189,190,191,192,169,193,194,195],"shipping and launch","shipping","launch","time","beta","rollout","rollback plan","monitoring","rollback strategy","plan","change","exposure","plano","ship","Tools","pre-launch",{"skillNo":197,"name":198,"nameZh":6,"description":199,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":200,"categories":201,"keywords":207,"publishedAt":38},"01M71HFXGHP092ZKFR762M9394","security-and-hardening","Hardens code against vulnerabilities using the OWASP Top 10 and a three-tier boundary system (input, processing, output). Use when handling user input, authentication, data storage, or external integrations. EN triggers: \u002Fsecurity, security audit, OWASP, three-tier boundary, least privilege, input validation. PT triggers: \u002Fsegurança, auditoria de segurança, OWASP, privilégio mínimo, validar entrada. Do NOT use for: non-security performance work (use \u002Freview's performance axis), or dependency updates that have no security exposure.","https:\u002F\u002Fgithub.com\u002Frunecraftai\u002Farcanum\u002Fblob\u002Fmain\u002Fpackages\u002Fspells\u002Fskills\u002Fsecurity-and-hardening\u002FSKILL.md",[202],{"categoryNo":203,"categoryName":204,"taxonomyType":205,"taxonomyTypeDesc":206},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[208,209,210,34,211,191,169,212,37,213,35,214,215,28,26,216,217,218,219,220],"security","hardening","security and hardening","authentication","input","three","system","least privilege","privilege","validation","vulnerabilities","user","processing"]