Announcing: A Free Book, A New Course, A Huge Price Cut...
It's a massive ship day. We're launching a free TypeScript book, new course, giveaway, price cut, and sale.
As an industry, we've started to agree that TypeScript is a good idea. It's a great way to catch bugs in your application code before they get to production.
But in many codebases, a huge part of your code isn't your application code. It's your tests. And in those tests, using TypeScript can be pretty painful.
Tests don't need to live up to the same level of strictness as the rest of your application code.
If you've got a broken type in your app, that'll likely end up in your users finding a bug. But what happens if your tests are passing, but you've got a broken type?
That broken type might be an indication that your tests are wrong - so you should probably check it. But once you've verified that it's all OK, it can feel like meaningless busywork.
Sometimes, your tests will need to test that when a function receives the wrong thing, it behaves in the right way.
Often the only way to achieve this is with an as any
:
const func = (a: string): void => {
if (typeof a !== "string") {
throw new Error("Oh dear!");
}
};
it("Throws an error when passed a number", () => {
expect(() => func(123 as any)).toThrow();
});
You might have an ESLint rule enabled to prevent 'any' from your tests. If you do, you might look for the as unknown as string
pattern:
it("Throws an error when passed a number", () => {
expect(() =>
func(123 as unknown as string)
).toThrow();
});
This is extremely un-ergonomic - a DOUBLE assertion, just to be able to do something extremely common. If you've got experience with heavily tested codebases, you'll know how common these assertions are.
Another extremely common pattern is testing a function which receives a lot of properties, but you only care about a few of them. This can happen often with external libraries, such as express
endpoints:
import { Request, Response } from "express";
const handler = (
req: Request,
res: Response
): void => {};
In this case, you might only care about the req.body
property. But you still need to pass the entire Request
type to the function. So, you'll end up doing as unknown as Request
a lot.
Not only is this annoying, but you also need to import the type you want to use everywhere you perform the test. A thousand tiny cuts.
shoehorn
I've been through dozens of open-source application codebases, and all of them who used a large test suite had this problem. So, I've created a solution.
I've released a library, shoehorn
, for easing the pain of working with tests in TypeScript.
It gives you a first-class way to pass the wrong type to a function.
import { fromPartial } from "@total-typescript/shoehorn";
it("Should get the user", () => {
handler(
// Mock the request!
fromPartial({
body: {
id: "123",
},
}),
// Mock the response!
fromPartial({})
);
});
You can install shoehorn
from npm, and check out the GitHub repo.
I can't wait to see how you use it!
Share this article with your friends
It's a massive ship day. We're launching a free TypeScript book, new course, giveaway, price cut, and sale.
Learn why the order you specify object properties in TypeScript matters and how it can affect type inference in your functions.
Learn how to use corepack
to configure package managers in Node.js projects, ensuring you always use the correct one.
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
Discover when it's appropriate to use TypeScript's any
type despite its risks. Learn about legitimate cases where any
is necessary.
Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.