[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71Q81PVSDFJ17SKWBJX0STQ":3,"skill-same:01M71Q81PVSDFJ17SKWBJX0STQ":58},{"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":25,"linkOnly":26,"markdownContent":27,"markdownBody":28,"frontMatter":29,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":35,"ownerHtmlUrl":36,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":37,"install":38,"resources":51},"01M71Q81PVSDFJ17SKWBJX0STQ","migrate-to-shoehorn",null,"将测试文件从 `as` 类型断言迁移到 @total-typescript\u002Fshoehorn。适用于用户提到 shoehorn、想替换测试中的 `as`，或需要局部测试数据时。","vinvcn\u002Fmattpocock-skills-zh-CN","vinvcn","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F120928190?v=4","01M71Q60C4V98DEXHGN2VXFSZB","01M71Q60D6PRAJHKQRY33ZNE59",5699,"MIT","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fmisc\u002Fmigrate-to-shoehorn\u002FSKILL.md",[],[18,19,20,21,22,23,24],"migrate to shoehorn","migrate","testing","typescript","as","文件","数据","2026-11-10 13:02:02",false,"---\nname: migrate-to-shoehorn\ndescription: 将测试文件从 `as` 类型断言迁移到 @total-typescript\u002Fshoehorn。适用于用户提到 shoehorn、想替换测试中的 `as`，或需要局部测试数据时。\n---\n\n# 迁移到 Shoehorn\n\n## 为什么用 shoehorn？\n\n`shoehorn` 允许你在 tests 中传入 partial data，同时保持 TypeScript 满意。它用 type-safe alternatives 替换 `as` assertions。\n\n**只用于 test code。** 永远不要在 production code 中使用 shoehorn。\n\nTests 中 `as` 的问题：\n\n- 经过训练，不去使用它\n- 必须手动指定 target type\n- 对故意错误的数据需要 double-as（`as unknown as Type`）\n\n## 安装\n\n```bash\nnpm i @total-typescript\u002Fshoehorn\n```\n\n## 迁移模式\n\n### 只需要少数属性的大型 objects\n\nBefore:\n\n```ts\ntype Request = {\n  body: { id: string };\n  headers: Record\u003Cstring, string>;\n  cookies: Record\u003Cstring, string>;\n  \u002F\u002F ...20 more properties\n};\n\nit(\"gets user by id\", () => {\n  \u002F\u002F Only care about body.id but must fake entire Request\n  getUser({\n    body: { id: \"123\" },\n    headers: {},\n    cookies: {},\n    \u002F\u002F ...fake all 20 properties\n  });\n});\n```\n\nAfter:\n\n```ts\nimport { fromPartial } from \"@total-typescript\u002Fshoehorn\";\n\nit(\"gets user by id\", () => {\n  getUser(\n    fromPartial({\n      body: { id: \"123\" },\n    }),\n  );\n});\n```\n\n### `as Type` → `fromPartial()`\n\nBefore:\n\n```ts\ngetUser({ body: { id: \"123\" } } as Request);\n```\n\nAfter:\n\n```ts\nimport { fromPartial } from \"@total-typescript\u002Fshoehorn\";\n\ngetUser(fromPartial({ body: { id: \"123\" } }));\n```\n\n### `as unknown as Type` → `fromAny()`\n\nBefore:\n\n```ts\ngetUser({ body: { id: 123 } } as unknown as Request); \u002F\u002F wrong type on purpose\n```\n\nAfter:\n\n```ts\nimport { fromAny } from \"@total-typescript\u002Fshoehorn\";\n\ngetUser(fromAny({ body: { id: 123 } }));\n```\n\n## 何时使用哪一个\n\n| Function        | Use case                                           |\n| --------------- | -------------------------------------------------- |\n| `fromPartial()` | 传入仍能 type-check 的 partial data                |\n| `fromAny()`     | 传入故意错误的数据（保留 autocomplete）             |\n| `fromExact()`   | 强制 full object（之后可换成 fromPartial）          |\n\n## 工作流\n\n1. **Gather requirements** - 询问用户：\n   - 哪些 test files 中的 `as` assertions 造成问题？\n   - 是否在处理大型 objects，但只关心部分 properties？\n   - 是否需要传入故意错误的数据来测试 error paths？\n\n2. **Install and migrate**：\n   - [ ] Install: `npm i @total-typescript\u002Fshoehorn`\n   - [ ] 查找 test files 中的 `as` assertions: `grep -r \" as [A-Z]\" --include=\"*.test.ts\" --include=\"*.spec.ts\"`\n   - [ ] 用 `fromPartial()` 替换 `as Type`\n   - [ ] 用 `fromAny()` 替换 `as unknown as Type`\n   - [ ] 添加来自 `@total-typescript\u002Fshoehorn` 的 imports\n   - [ ] 运行 type check 验证\n","# 迁移到 Shoehorn\n\n## 为什么用 shoehorn？\n\n`shoehorn` 允许你在 tests 中传入 partial data，同时保持 TypeScript 满意。它用 type-safe alternatives 替换 `as` assertions。\n\n**只用于 test code。** 永远不要在 production code 中使用 shoehorn。\n\nTests 中 `as` 的问题：\n\n- 经过训练，不去使用它\n- 必须手动指定 target type\n- 对故意错误的数据需要 double-as（`as unknown as Type`）\n\n## 安装\n\n```bash\nnpm i @total-typescript\u002Fshoehorn\n```\n\n## 迁移模式\n\n### 只需要少数属性的大型 objects\n\nBefore:\n\n```ts\ntype Request = {\n  body: { id: string };\n  headers: Record\u003Cstring, string>;\n  cookies: Record\u003Cstring, string>;\n  \u002F\u002F ...20 more properties\n};\n\nit(\"gets user by id\", () => {\n  \u002F\u002F Only care about body.id but must fake entire Request\n  getUser({\n    body: { id: \"123\" },\n    headers: {},\n    cookies: {},\n    \u002F\u002F ...fake all 20 properties\n  });\n});\n```\n\nAfter:\n\n```ts\nimport { fromPartial } from \"@total-typescript\u002Fshoehorn\";\n\nit(\"gets user by id\", () => {\n  getUser(\n    fromPartial({\n      body: { id: \"123\" },\n    }),\n  );\n});\n```\n\n### `as Type` → `fromPartial()`\n\nBefore:\n\n```ts\ngetUser({ body: { id: \"123\" } } as Request);\n```\n\nAfter:\n\n```ts\nimport { fromPartial } from \"@total-typescript\u002Fshoehorn\";\n\ngetUser(fromPartial({ body: { id: \"123\" } }));\n```\n\n### `as unknown as Type` → `fromAny()`\n\nBefore:\n\n```ts\ngetUser({ body: { id: 123 } } as unknown as Request); \u002F\u002F wrong type on purpose\n```\n\nAfter:\n\n```ts\nimport { fromAny } from \"@total-typescript\u002Fshoehorn\";\n\ngetUser(fromAny({ body: { id: 123 } }));\n```\n\n## 何时使用哪一个\n\n| Function        | Use case                                           |\n| --------------- | -------------------------------------------------- |\n| `fromPartial()` | 传入仍能 type-check 的 partial data                |\n| `fromAny()`     | 传入故意错误的数据（保留 autocomplete）             |\n| `fromExact()`   | 强制 full object（之后可换成 fromPartial）          |\n\n## 工作流\n\n1. **Gather requirements** - 询问用户：\n   - 哪些 test files 中的 `as` assertions 造成问题？\n   - 是否在处理大型 objects，但只关心部分 properties？\n   - 是否需要传入故意错误的数据来测试 error paths？\n\n2. **Install and migrate**：\n   - [ ] Install: `npm i @total-typescript\u002Fshoehorn`\n   - [ ] 查找 test files 中的 `as` assertions: `grep -r \" as [A-Z]\" --include=\"*.test.ts\" --include=\"*.spec.ts\"`\n   - [ ] 用 `fromPartial()` 替换 `as Type`\n   - [ ] 用 `fromAny()` 替换 `as unknown as Type`\n   - [ ] 添加来自 `@total-typescript\u002Fshoehorn` 的 imports\n   - [ ] 运行 type check 验证\n",{"name":5,"description":7},"main","c5c5e87cc1df26aeb7dba345ad9f846434d2b3d4","skills\u002Fmisc\u002Fmigrate-to-shoehorn",444,"https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN","https:\u002F\u002Fskills.sh\u002Fvinvcn\u002Fmattpocock-skills-zh-CN","https:\u002F\u002Fgithub.com\u002Fvinvcn","2026-11-10 17:40:42",{"source":8,"skillName":5,"commands":39,"prompt":49,"zipAvailable":50},[40,43,46],{"tool":41,"command":42},"npx","npx skills add vinvcn\u002Fmattpocock-skills-zh-CN --skill migrate-to-shoehorn",{"tool":44,"command":45},"bunx","bunx skills add vinvcn\u002Fmattpocock-skills-zh-CN --skill migrate-to-shoehorn",{"tool":47,"command":48},"pnpm","pnpm dlx skills add vinvcn\u002Fmattpocock-skills-zh-CN --skill migrate-to-shoehorn","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fmisc\u002Fmigrate-to-shoehorn\u002FSKILL.md",true,[52],{"path":53,"resourceType":54,"resourceTypeDesc":55,"sizeBytes":56,"sourceHtmlUrl":57},"agents\u002Fopenai.yaml","REFERENCE","参考文档",104,"https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fmisc\u002Fmigrate-to-shoehorn\u002Fagents\u002Fopenai.yaml",[59,83,93,103,113,122],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":78,"publishedAt":25},"01M71Q8F3M8YFP6WYVYQP61KBT","writing-for-agents","为 agent 编写文档。适用于创建或编辑 skills，或修改 AGENTS.md 或 CLAUDE.md 时。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fwriting-for-agents\u002FSKILL.md",[65,70,73],{"categoryNo":66,"categoryName":67,"taxonomyType":68,"taxonomyTypeDesc":69},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":71,"categoryName":72,"taxonomyType":68,"taxonomyTypeDesc":69},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":74,"categoryName":75,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[79,80,81,82],"for","writing for agents","writing","文档",{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":25},"01M71Q8DHRPYE6R648CQ7NAHYN","wait-what","停一下：刚才那条消息没有说清楚，重新表述一遍。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fwait-what\u002FSKILL.md",[],[90,91,92],"wait","wait what","true",{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":25},"01M71Q8C8CBSRJRP8Y0JRZ7RJ7","to-questionnaire","把你无法完整回答的 decision 转成一份交给他人填写的 questionnaire。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fto-questionnaire\u002FSKILL.md",[],[100,101,102,92],"questionnaire","to questionnaire","decision",{"skillNo":104,"name":105,"nameZh":6,"description":106,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":112,"publishedAt":25},"01M71Q8ASS9A6P84PSC02YFKHB","teach","在这个工作区中教用户一个新技能或概念。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fteach\u002FSKILL.md",[109],{"categoryNo":110,"categoryName":111,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000039","教师与教育",[105,92],{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":119,"publishedAt":25},"01M71Q89C2C2QRR3J3H64ZKH0P","handoff","把当前对话压缩成交接文档，让另一个代理接手。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fhandoff\u002FSKILL.md",[],[115,92,120,121,82],"对话","压缩",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":25},"01M71Q883CXWF0BGDQC3Y8TJZ2","grilling","围绕计划、decision 或 idea 持续追问用户。适用于用户想对自己的思路做压力测试，或使用任何 “grill” 触发措辞时。","https:\u002F\u002Fgithub.com\u002Fvinvcn\u002Fmattpocock-skills-zh-CN\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fgrilling\u002FSKILL.md",[],[124,129,130,102,20],"idea","grill"]