[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7B2Y0BESNNXSY4M7CEJVCJ3":3,"skill-same:01M7B2Y0BESNNXSY4M7CEJVCJ3":55},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":27,"commitSha":28,"skillDirectory":29,"forksCount":30,"repositoryHtmlUrl":31,"repositoryHomepageUrl":6,"ownerHtmlUrl":32,"ownerBlogUrl":33,"ownerDescription":6,"updatedAt":34,"install":35,"resources":48},"01M7B2Y0BESNNXSY4M7CEJVCJ3","typescript-best-practices",null,"TypeScript best practices. Use when reading or editing any .ts or .tsx file.","painhardcore\u002Fpstack","painhardcore","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F4718161?v=4","01M7B2JS5QJR4B3VVXX0X6NKC5","Andrei Iurchenkov","01M7B2JS6S2BCFD0FCVZZNMEZZ",22,"MIT","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Ftypescript-best-practices\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: typescript-best-practices\ndescription: TypeScript best practices. Use when reading or editing any .ts or .tsx file.\npaths: [\"**\u002F*.ts\", \"**\u002F*.tsx\"]\n---\n\n# TypeScript best practices\n\nApply the **type-system-discipline** principle skill first.\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 the boundary. A brand comes from a verifying type guard (`input is AgentId`), never from `as`. |\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\u003CT>` only where the loose type forces `!`, a cast, or a \"should never happen\" throw. |\n| `unknown` over `any` | External data is `unknown`. |\n| Schemas before guards | Before hand-writing a property-by-property type guard, use the repository's runtime schema library and infer the type from the schema, such as `z.infer`. |\n| No `as` casts | Every `as` is a runtime crash waiting. Cast only after validation. |\n| Narrowing hierarchy | Discriminant switch > `in` operator > `typeof`\u002F`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 | Parse where data crosses in, into a named domain type. `Record\u003Cstring, unknown>` (however spelled) stops at that parse. Trust types inside. See the **boundary-discipline** principle skill. |\n| Schema-derived types | Reach for `Pick`\u002F`Omit`\u002F`Parameters`\u002F`ReturnType`\u002F`Awaited`\u002F`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\u002Fdisposable 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\u002Fpatterns.md`.\n","# TypeScript best practices\n\nApply the **type-system-discipline** principle skill first.\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 the boundary. A brand comes from a verifying type guard (`input is AgentId`), never from `as`. |\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\u003CT>` only where the loose type forces `!`, a cast, or a \"should never happen\" throw. |\n| `unknown` over `any` | External data is `unknown`. |\n| Schemas before guards | Before hand-writing a property-by-property type guard, use the repository's runtime schema library and infer the type from the schema, such as `z.infer`. |\n| No `as` casts | Every `as` is a runtime crash waiting. Cast only after validation. |\n| Narrowing hierarchy | Discriminant switch > `in` operator > `typeof`\u002F`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 | Parse where data crosses in, into a named domain type. `Record\u003Cstring, unknown>` (however spelled) stops at that parse. Trust types inside. See the **boundary-discipline** principle skill. |\n| Schema-derived types | Reach for `Pick`\u002F`Omit`\u002F`Parameters`\u002F`ReturnType`\u002F`Awaited`\u002F`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\u002Fdisposable 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\u002Fpatterns.md`.\n",{"name":5,"paths":24,"description":7},[25,26],"**\u002F*.ts","**\u002F*.tsx","master","7f2d936bba86c2c512c4ad83e759752f4f065b7c","skills\u002Ftypescript-best-practices",4,"https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack","https:\u002F\u002Fgithub.com\u002Fpainhardcore","https:\u002F\u002Fiurchenkov.com","2026-11-14 06:02:01",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add painhardcore\u002Fpstack --skill typescript-best-practices",{"tool":41,"command":42},"bunx","bunx skills add painhardcore\u002Fpstack --skill typescript-best-practices",{"tool":44,"command":45},"pnpm","pnpm dlx skills add painhardcore\u002Fpstack --skill typescript-best-practices","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Ftypescript-best-practices\u002FSKILL.md",true,[49],{"path":50,"resourceType":51,"resourceTypeDesc":52,"sizeBytes":53,"sourceHtmlUrl":54},"references\u002Fpatterns.md","REFERENCE","参考文档",9287,"https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Ftypescript-best-practices\u002Freferences\u002Fpatterns.md",[56,63,70,77,84,91],{"skillNo":57,"name":58,"nameZh":6,"description":59,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":60,"categories":61,"keywords":62,"publishedAt":19},"01M7B2Y4B9GZTVRQQEN6PQJJ8R","why","Use for 'why does X work this way', 'why we picked Y', design rationale, regressions, postmortems, or data-backed thresholds. Discovers available MCPs and queries each evidence category (source control, issue tracker, long-form docs, real-time chat, infrastructure observability, error tracking, product analytics warehouse) in parallel, then returns a cited read on decisions and tradeoffs. Use how for runtime behavior.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Fwhy\u002FSKILL.md",[],[],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":69,"publishedAt":19},"01M7B2Y2G4D64TNX6JW7N87BKZ","unslop","Cut AI tells from any writing.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Funslop\u002FSKILL.md",[],[],{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":76,"publishedAt":19},"01M7B2XVPCP4X4B9ME462ZQGSP","technical-writing","Layered technical-writing standard: Diátaxis structure, Google developer style sentences, STE instruction rules, Global English syntax. Use for \u002Ftechnical-writing or when writing or reviewing docs, RFCs, readmes, PR descriptions, or commit messages.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Ftechnical-writing\u002FSKILL.md",[],[],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":19},"01M7B2XRE370P92ZQF9SJG3A5W","teach","Explain a body of work plainly so a person actually understands it. Runs the `how` and `why` skills and weaves what they find into one clear explanation. Use for 'teach me this', 'help me really understand X', 'explain this change or subsystem to me'.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Fteach\u002FSKILL.md",[],[],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":19},"01M7B2XN6VE4EZTQ84NWS7P0Q6","tdd","Use only when the user explicitly asks for TDD, a failing test, or a regression test, OR when the bug has an obvious cheap local test target. Skip when the test path is unclear, expensive, integration-heavy, or not requested.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Ftdd\u002FSKILL.md",[],[],{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":19},"01M7B2XJMP6NYMG6W0FSK36R28","swarm","Fan out N independent workers, collect their results, and return one report for coverage, races, gauntlets, or exploration.","https:\u002F\u002Fgithub.com\u002Fpainhardcore\u002Fpstack\u002Fblob\u002Fmaster\u002Fskills\u002Fswarm\u002FSKILL.md",[],[]]