# How to Migrate Type Assertions to Shoehorn for Safer TypeScript Tests

> Migrate TypeScript type assertions to shoehorn for safer testing. Replace brittle `as` with `@total-typescript/shoehorn` helpers to reduce boilerplate and maintain type safety.

- Repository: [Matt Pocock/skills](https://github.com/mattpocock/skills)
- Tags: migration-guide
- Published: 2026-04-04

---

**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`](https://github.com/mattpocock/skills/blob/main/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`](https://github.com/mattpocock/skills/blob/main/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

```bash
npm i @total-typescript/shoehorn

```

### 2. Locate Existing Assertions

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

```bash
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.

```ts
// 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()`.

```ts
// 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.

```ts
// 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:

```ts
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:

```ts
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`](https://github.com/mattpocock/skills/blob/main/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.