shoehorn
npm i @total-typescript/shoehorn
shoehorn
(meaning "to force something into a space") lets you pass partial data in tests while keeping TypeScript happy.
Problem
Using 'as' in tests feels bad.
type Request = {
body: {
id: string;
};
};
it("Should get the user", () => {
getUser({
body: {
id: "123",
},
} as Request);
});
- You're trained not to use it
- You need to manually specify the type you want to assert to
- For testing with incorrect data, you need to 'double-as' (
as unknown as User
)
Solution
shoehorn
gives you some first-class primitives for safely providing incomplete data to tests.
import { fromPartial } from "@total-typescript/shoehorn";
it("Should get the user", () => {
getUser(
fromPartial({
body: {
id: "123",
},
}),
);
});
But isn't passing partial data to tests bad?
Yes, in general. Having to pass huge objects to tests is a sign that your types are too loose. Ideally, every function should only specify the data it needs.
Unfortunately, we live in the real world. There are many cases where shoehorn
is the best choice:
- Legacy codebases: If you're working on a large codebase, you might not have the time to refactor everything to be perfect.
- Third-party libraries: If you're using a third-party library, you might not be able to alter the types without needless wrapper functions.
API
For each example below, imagine that the following types are defined:
type Request = {
body: {
id: string;
};
};
const requiresRequest = (request: Request) => {};
fromPartial
Lets you pass a deep partial to a slot expecting a type.
import { fromPartial } from "@total-typescript/shoehorn";
requiresRequest(
fromPartial({
body: {
id: "123",
},
}),
);
It'll fail if you pass a type that doesn't match the one expected:
requiresRequest(fromPartial("1234123"));
fromAny
Lets you pass anything to a slot, while still giving you autocomplete on the original type:
import { fromAny } from "@total-typescript/shoehorn";
requiresRequest(
fromAny({
body: {
id: 124123,
},
}),
);
It WILL NOT FAIL if you pass something that doesn't match.
requiresRequest(fromAny("1234123"));
fromExact
A convenience method for forcing you to pass all the properties of a type. Useful for when you want to swap in and out of fromPartial
/fromAny
:
import { fromExact } from "@total-typescript/shoehorn";
requiresRequest(
fromExact({
body: {
id: 124123,
},
}),
);