{"id":18124,"plugin_id":"plugins_6a85a87df50c8191bd7f010bb7b17794","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:14:38.123Z","digest":"0b62d4144656c39d0965ff440bb9de4e6c42deeccb9e7d73b1aea0574064a9e9","against":null,"payload":{"description":"TypeScript best practices. Use when reading or editing any .ts or .tsx file.","included_files":[{"relative_path":"references/patterns.md","size_in_bytes":8695}],"name":"typescript-best-practices","skill_md_contents":"---\nname: typescript-best-practices\ndescription: TypeScript best practices. Use when reading or editing any .ts or .tsx file.\n---\n\n# TypeScript best practices\n\nApply the **type-system-discipline** principle skill first; this skill grounds it in TypeScript syntax.\n\n| Rule | Summary |\n|------|---------|\n| Discriminated unions | Model variants with a `kind` literal discriminant so impossible states can't be represented. No optional-field bags. |\n| Branded types | Brand primitives with `& { readonly __brand: \"X\" }` so they can't be mixed up. Validate once at creation. |\n| Constructive modeling | Build the shape so the illegal value can't be constructed. `[T, ...T[]]` for non-empty, `[T, T][]` for even length, `start` plus `duration` for a range. Not a runtime guard, not a wish for refinement types. |\n| Simplest total type | Keep `T[]` while every operation on it stays total. Strengthen to `NonEmpty<T>` only where the loose type forces `!`, a cast, or a \"should never happen\" throw. |\n| `unknown` over `any` | External data is `unknown`. `any` disables type checking everywhere it touches. |\n| No `as` casts | Every `as` is a runtime crash waiting. Cast only after validation. |\n| Narrowing hierarchy | Discriminant switch > `in` operator > `typeof`/`instanceof` > user-defined type guard > `as`. |\n| Type guards | Must verify the claim. A lying guard is worse than `as` because the bug hides behind a name that says it's safe. Name them `isX` or `hasX`. |\n| Exhaustiveness | Inline `const _exhaustive: never = x;` in default arms so the compiler errors when a new variant is added. |\n| `satisfies` over `as` | Validates the value without widening literal types. |\n| Boundary validation | Validate where data crosses in; trust types inside. See the **boundary-discipline** principle skill. |\n| Schema-derived types | Reach for `Pick`/`Omit`/`Parameters`/`ReturnType`/`Awaited`/`typeof` before declaring a new interface. |\n| Object args | Pass objects, not positional, so argument order is self-documenting. Skip on hot paths (per-frame render, tokenizers, parsers). |\n| Real tests | Don't mock what you can run. Prefer the framework's real test primitives with leak/disposable checks, and verify UI in a running build. Mock only what you can't run locally. |\n| Structured telemetry | Prefer structured logger diagnostics with enough context to debug from an id. No `console.log` in shipped code. |\n\nExamples: `references/patterns.md`.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}