How to Migrate Type Assertions to Shoehorn for Safer TypeScript Tests

Replace brittle as type assertions in your TypeScript tests with @total-typescript/shoehorn helpers like fromPartial() and fromAny() to maintain type safety while reducing boilerplate and eliminating the error-prone as unknown as double-cast pattern.

The mattpocock/skills repository provides a dedicated migrate-to-shoehorn skill that walks you through migrating type assertions to Shoehorn for better type testing. This skill, documented in migrate-to-shoehorn/SKILL.md, replaces unsafe as casts with purpose-built utilities that keep your test code expressive and your compiler checks strong.

Why Replace as Assertions in Tests?

Raw as type assertions create friction in test suites for three specific reasons. First, they require manual type matching, forcing you to write out every property of a type even when only a subset is needed for the test. Second, they encourage the double-cast pattern as unknown as Type when you need to pass intentionally incorrect data for negative-case testing, which completely defeats TypeScript’s static analysis. Third, they reduce training and readability—as casts are discouraged in most production codebases and create noise that distracts from test intent.

The Three Shoehorn Helpers

Shoehorn provides three distinct utilities to handle different testing scenarios:

  • fromPartial() – Builds an object that satisfies a type while only requiring the properties you actually need to specify.
  • fromAny() – Allows deliberately incorrect data while preserving autocomplete support, replacing the as unknown as escape hatch.
  • fromExact() – Forces a complete object shape when you want to be explicit about all properties.

Step-by-Step Migration Workflow

The migrate-to-shoehorn/SKILL.md file outlines a repeatable five-step process (checklist lines 112–118) to migrate your test suite safely.

1. Install the Dependency

npm i @total-typescript/shoehorn

2. Locate Existing Assertions

Run the suggested grep command to find type assertions in your test files:

grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts"

3. Apply the Appropriate Replacement Pattern

Large objects with minimal requirements:

Replace verbose manual construction with fromPartial() to supply only the properties your test actually touches.

// Before
getUser({
  body: { id: "123" },
  headers: {},
  cookies: {},
  // …20 more properties
} as Request);

// After
import { fromPartial } from "@total-typescript/shoehorn";

getUser(
  fromPartial({
    body: { id: "123" },
  })
);

Simple direct casts:

Remove the as keyword entirely and wrap the object in fromPartial().

// Before
getUser({ body: { id: "123" } } as Request);

// After
getUser(fromPartial({ body: { id: "123" } }));

Intentional type mismatches (double-casts):

Replace as unknown as Type with fromAny() to signal that the data is deliberately wrong for negative testing.

// Before
getUser({ body: { id: 123 } } as unknown as Request);

// After
import { fromAny } from "@total-typescript/shoehorn";

getUser(fromAny({ body: { id: 123 } }));

4. Update Imports

Ensure each test file imports the needed helpers from @total-typescript/shoehorn.

5. Verify with the Compiler

Run the TypeScript compiler to confirm that no type errors remain and that your migrations maintain the intended test coverage.

Practical Code Examples

Here are complete, runnable patterns you can copy directly into your test suite.

Testing with Partial Data

Use fromPartial() when you only care about specific request properties:

import { fromPartial } from "@total-typescript/shoehorn";

it("gets user by id", () => {
  getUser(
    fromPartial({
      body: { id: "123" },
    })
  );
});

Handling Intentionally Wrong Types

Use fromAny() when you need to pass malformed data to test error handling:

import { fromAny } from "@total-typescript/shoehorn";

it("rejects numeric ids", () => {
  getUser(fromAny({ body: { id: 123 } }));
});

Summary

  • Install @total-typescript/shoehorn to access type-safe test helpers.
  • Locate existing as assertions using the grep command for *.test.ts and *.spec.ts files.
  • Replace as Type with fromPartial() and as unknown as Type with fromAny().
  • Verify your changes by running the TypeScript compiler to ensure no regressions.
  • Reference the migrate-to-shoehorn/SKILL.md file in the mattpocock/skills repository for the full checklist and additional context.

Frequently Asked Questions

What is Shoehorn and why is it better than type assertions?

Shoehorn is a TypeScript utility library from Total TypeScript that provides fromPartial(), fromAny(), and fromExact() helpers. Unlike as assertions, which bypass the compiler’s type checking, Shoehorn helpers maintain static analysis while allowing flexible test data construction.

How do I find all type assertions in my test files?

Run the grep command grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts" from your project root. This searches for the pattern where a capital letter follows the as keyword, which captures most TypeScript type assertion syntax.

What is the difference between fromPartial() and fromAny()?

fromPartial() requires that the properties you provide match the target type’s structure, but lets you omit optional properties. fromAny() places no constraints on the shape, making it ideal for negative test cases where you want to verify error handling with malformed data.

Can I migrate gradually or must I replace all assertions at once?

You can migrate gradually. Shoehorn utilities are drop-in replacements for specific assertions, so you can update one test file at a time. The TypeScript compiler will validate each migration immediately, ensuring no runtime behavior changes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →