tintype
v0.1.1
Published
Inline type snapshot testing for vitest
Readme
tintype is a tiny vitest plugin that snapshots
inferred TypeScript types. It piggybacks on
toMatchInlineSnapshot,
but rather than snapshotting the value, it injects the inferred type from
TypeScript's language service.
install
npm install tintypesetup
// vitest.config.ts
import tintype from "tintype/plugin";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [tintype()],
test: { setupFiles: ["tintype/setup"] },
});usage
// math.test.ts
import { expectType } from "tintype";
import { add } from "./math.js";
test("add returns a number", () => {
expectType(add(1, 2)).toMatchInlineSnapshot(`number`);
});It gets more interesting with narrowing and generics:
import { assert } from "vitest";
import { expectType } from "tintype";
import { isUser } from "./guards.js";
test("isUser narrows to User", () => {
const user: unknown = { name: "alice", role: "admin" };
assert(isUser(user), "Expected user");
expectType(user).toMatchInlineSnapshot(`User`);
});import { expectType } from "tintype";
declare function query<T extends Record<string, unknown>>(
table: T[],
key: keyof T,
): Pick<T, typeof key>[];
test("query narrows to picked fields", () => {
const data = [{ id: 1, name: "a", score: 0.5 }];
expectType(query(data, "name")).toMatchInlineSnapshot(
`Pick<{ id: number; name: string; score: number; }, "name">[]`,
);
});Run vitest --update and the snapshot fills itself in. If the inferred type
ever changes, the snapshot drifts and the test fails.
options
tintype({
tsconfig: "./tsconfig.test.json",
formatCommand: "prettier --parser typescript",
});| option | default | description |
| --------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| tsconfig | auto-detected | Path to tsconfig.json for type resolution |
| formatCommand | none | Shell command to format type strings. Each type is wrapped as type __tintype__ = <type>; and piped via stdin. Use {filename} for the file path. |
motivation
I love snapshot testing. I also write libraries that make heavy use of
TypeScript's type inference (like quiver and
zarrita.js). TypeScript changes between
releases, and it's surprisingly hard to capture the preciseness of what your
types actually infer — or don't. tintype lets you write a test, run --update,
and get a snapshot of exactly what TypeScript thinks. When inference breaks or
drifts, you see it in the diff.
At transform time, tintype finds expectType(expr) calls and uses TypeScript's
language service to resolve the type of expr. It rewrites the call to a plain
expect with the resolved type string, so vitest's snapshot machinery takes
over from there.
development
This project uses Vite+.
vp check # format, lint, and type check
vp test # run tests
vp run build # build with tsclicense
MIT
