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.
If you're working on library code, then you definitely want to be testing your types. Types are a crucial part of your API that you're exposing to users, and you want to make sure that your logic on the type level works.
One approach you can take is testing your types with vitest - an extremely powerful and popular test runner based on Vite. Vitest is already widely used for testing runtime code, so why not use it for testing your types too? Vitest ships with several APIs that let you test types.
import { assertType, expectTypeOf } from "vitest";
import { mount } from "./mount";
test("my types work properly", () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
});
This is pretty cool and having it built into a test runner is really valuable, because it lets you colocate your type tests with your runtime tests.
But if you're not using vitest, you can roll your own solution.
Let's say you're testing the type of something. You can use type helpers like Expect
and Equal
to be really specific with the type that you're trying to narrow to.
type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? true
: false;
// Example
const identityFunc = <T>(arg: T) => arg;
it("Should return whatever you pass in", () => {
const test1 = identityFunc("hello");
type test = Expect<Equal<typeof test1, "hello">>;
});
If you get an error, it'll show up as a red line in your IDE. As part of your test suite, you can then run tsc
(the TypeScript CLI) on the entire test suite and check if any of your type tests fail.
You can also check that something doesn't work in TypeScript by using ts-expect-error
. This is a special comment that you can add on a line to look at the next line. It'll fail if it doesn't see an error on the next line. You can use this to check if something is allowed or not and assert that an error is thrown in certain situations.
const myFunc = <T extends string>(arg: T) => {};
it("Should not accept a number", () => {
// @ts-expect-error
myFunc(123);
});
It's not exactly perfect since it doesn't let you be that granular - you can't check that a specific error is thrown, only that an error has been thrown.
tsd
Lastly, there's a really nice library called tsd which helps you test your types. It's actually pretty similar to the vitest runner, but it bundles in everything and makes it nice to work with. I don't have as much experience with it, but it's definitely worth a look.
Those are three ways that you should be thinking about testing your types. It's worth noting that for application development, you're rarely going to be in a position where testing your types will be worth it.
But if you're working with any kind of library that is going to be consumed by a lot of people, then learning how to test types - and getting good at it - is critical.
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.