migrate-to-shoehorn
- 信任分
- 88/100
- 兼容 Agent
- 1
- 领域
- 工程开发
- 兼容 Agent
- Claude Code
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @mattpocock · 未声明 license
- 安装命令数
- 1 条
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
想读作者英文原文? ↓ 滚到正文区切换 · 在 GitHub 查看 ↗
设计思路
migrate-to-shoehorn 是 mattpocock 的「测试代码 as 断言升级」专项 skill——把测试里 as Type / as unknown as Type 替换成 @total-typescript/shoehorn 的 fromPartial() / fromAny(),让 TS 仍然开心、又不必虚构整份大对象。只在测试代码里用,永不进生产。
为什么要换 shoehorn
as 在测试里的痛点:
- 团队规约通常禁用
as - 必须手动指定目标类型
- 故意传错数据时还得
as unknown as Type双断言
三个迁移模式
1. 大对象只关心少数属性
之前要塞 20 个伪属性才让 TS 闭嘴:
getUser({
body: { id: "123" },
headers: {},
cookies: {},
// ...还有一堆
});
之后:
import { fromPartial } from "@total-typescript/shoehorn";
getUser(fromPartial({ body: { id: "123" } }));
2. as Type → fromPartial()
直接替换。
3. as unknown as Type → fromAny()
故意传错类型时用 fromAny——保留 autocomplete。
选哪个
| 函数 | 适用场景 |
|---|---|
fromPartial() |
传部分数据但仍需 type-check |
fromAny() |
故意传错数据(错误路径测试) |
fromExact() |
强制完整对象(以后想换回 fromPartial 时) |
工作流
- 收集需求:问用户哪些测试文件
as出了问题?是不是大对象只用几条字段?是不是要测错误路径? - 安装迁移:
npm i @total-typescript/shoehorngrep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts"找断言点as Type→fromPartial()、as unknown as Type→fromAny()- 补 import
- 跑 type check 验证
适合谁
- 用 TypeScript 写测试、被
as拖累的工程师 - 在 monorepo 里清理测试代码异味的 Tech Lead
- 教学/分享 mattpocock 测试套路的人
何时不该用
- 项目不在 TS 上——本 skill 与
@total-typescript/shoehorn绑定 - 生产代码里塞
as——本 skill 明令仅限测试
配套
tdd / test-driven-development(写测试母法)、improve-codebase-architecture(结构性改 testability)、request-refactor-plan(大批量重构 plan 出口)。
Migrate to Shoehorn
Why shoehorn?
shoehorn lets you pass partial data in tests while keeping TypeScript happy. It replaces as assertions with type-safe alternatives.
Test code only. Never use shoehorn in production code.
Problems with as in tests:
- Trained not to use it
- Must manually specify target type
- Double-as (
as unknown as Type) for intentionally wrong data
Install
npm i @total-typescript/shoehorn
Migration patterns
Large objects with few needed properties
Before:
type Request = {
body: { id: string };
headers: Record<string, string>;
cookies: Record<string, string>;
// ...20 more properties
};
it("gets user by id", () => {
// Only care about body.id but must fake entire Request
getUser({
body: { id: "123" },
headers: {},
cookies: {},
// ...fake all 20 properties
});
});
After:
import { fromPartial } from "@total-typescript/shoehorn";
it("gets user by id", () => {
getUser(
fromPartial({
body: { id: "123" },
}),
);
});
as Type → fromPartial()
Before:
getUser({ body: { id: "123" } } as Request);
After:
import { fromPartial } from "@total-typescript/shoehorn";
getUser(fromPartial({ body: { id: "123" } }));
as unknown as Type → fromAny()
Before:
getUser({ body: { id: 123 } } as unknown as Request); // wrong type on purpose
After:
import { fromAny } from "@total-typescript/shoehorn";
getUser(fromAny({ body: { id: 123 } }));
When to use each
| Function | Use case |
|---|---|
fromPartial() |
Pass partial data that still type-checks |
fromAny() |
Pass intentionally wrong data (keeps autocomplete) |
fromExact() |
Force full object (swap with fromPartial later) |
Workflow
Gather requirements - ask user:
- What test files have
asassertions causing problems? - Are they dealing with large objects where only some properties matter?
- Do they need to pass intentionally wrong data for error testing?
- What test files have
Install and migrate:
- Install:
npm i @total-typescript/shoehorn - Find test files with
asassertions:grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts" - Replace
as TypewithfromPartial() - Replace
as unknown as TypewithfromAny() - Add imports from
@total-typescript/shoehorn - Run type check to verify
- Install: