TypeScript · Types that pay for themselves

How do you test TypeScript types?

Hardtestingtype-level-testsgenericsUpdated Oct 2026
Quiz this track

Short answer

Type-level tests assert that types behave as intended, using utilities like Expect, Equal and @ts-expect-error. They catch regressions in generic helpers that runtime tests cannot see.

Runtime tests check values; type tests check the compiler's view. A generic helper can still return the right runtime value while its type silently widens to any or loses a relationship. Type tests make those regressions fail in CI.

ts
type Equal<A, B> =
  (<T>() => T extends A ? 1 : 2) extends
  (<T>() => T extends B ? 1 : 2) ? true : false;

type Expect<T extends true> = T;

type Result = Expect<Equal<ReturnType<() => string>, string>>;

Techniques

  • Use a compile-time Equal helper for exact type comparisons
  • Use Expect to turn a false comparison into a type error
  • Use @ts-expect-error on calls that must be rejected
  • Use a library such as tsd or expect-type for a richer API
  • Run tsc in CI so type tests are part of the build

A type test should be small and focused. Test the public behavior of a generic helper rather than its internal implementation. If a test needs a deeply nested conditional type to express the expectation, the helper may be too complex for its use case.

Do not use @ts-ignore for negative tests. It suppresses any error, including an unrelated one. @ts-expect-error fails if no error occurs, which is what you want.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1What can a type-level test catch that a runtime test cannot?
  2. 2Why is @ts-expect-error better than @ts-ignore?
  3. 3How does the Equal helper work?
  4. 4When should type tests run in CI?

Say it in the interview

Type-level tests check that generic helpers produce the types I expect. I use an Equal helper and a compile-time assertion so a regression fails the type check, and I add ts-expect-error cases for inputs that must be rejected. Runtime tests cannot catch a return type silently widening to any. I run these checks in CI alongside the normal test suite.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live TypeScript mock interview and get scored feedback on depth, clarity and confidence.