Volume 4

Q219: Type Aware Testing, Contracts and Compile Time Safety

Difficulty: SeniorFrequency: MediumAnswer time: 8-12 minutes

What Interviewers Want To Evaluate

Interviewers want to know whether you understand how TypeScript and tests complement each other.

They check typechecking in CI, type-level tests, contract tests, runtime validation tests, component tests, fixture typing, mocks, and where compile-time safety does not replace behavioral coverage.

Short Interview Answer

TypeScript catches invalid code shapes at compile time, while tests prove runtime behavior. I run typecheck in CI, use typed fixtures and mocks, add contract tests for API/schema boundaries, and write type-level tests for shared utilities when the type behavior itself is the product. I do not replace user-flow or accessibility tests with TypeScript because types cannot prove runtime behavior.

Detailed Interview Answer

TypeScript and tests solve different problems.

TypeScript answers:

does this code satisfy known contracts
are properties available
are union states handled
are function inputs compatible
are public APIs used correctly

Tests answer:

does the UI behave correctly
does validation run at runtime
does the server reject bad input
does the user flow recover from failure
does performance stay within budget

You need both.

Typecheck In CI

Always run typecheck.

npm run typecheck

This protects the main branch from type regressions.

For large repos, consider:

incremental typecheck
project references
affected package checks
separate fast and full pipelines

The signal should be reliable and fast enough for daily use.

Typed Fixtures

Fixtures should satisfy real types.

type User = {
  id: string;
  name: string;
  role: "admin" | "member";
};

const userFixture = {
  id: "u1",
  name: "Ada",
  role: "admin",
} satisfies User;

satisfies checks shape while preserving useful literal types.

Mock Safety

Mocks should not drift from real APIs.

type LoadUser = (id: string) => Promise<User>;

const mockLoadUser: LoadUser = async (id) => ({
  id,
  name: "Ada",
  role: "member",
});

If the API changes, the mock should fail too.

Weak mocks create false confidence.

Type-Level Tests

Some utilities need type-level tests.

Examples:

typed route helpers
event payload maps
generic Select props
schema inference helpers
conditional type utilities

Conceptually, test compile-time expectations.

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

type Test = Expect<Equal<"a" | "b", "a" | "b">>;

Use dedicated tools if the project needs many of these.

Runtime Contract Tests

Runtime schemas need runtime tests.

test("rejects invalid user response", () => {
  const result = UserSchema.safeParse({ id: 1, name: null });
  expect(result.ok).toBe(false);
});

Types alone cannot prove schema behavior.

The schema is code.

Test it like code.

Component Contract Tests

Component prop types prevent invalid compile-time calls.

Component tests prove behavior.

does the disabled button block action
does error text connect to aria-describedby
does loading state preserve layout
does keyboard navigation work
does optimistic UI roll back on failure

Type safety improves setup, but user behavior still needs testing.

API Contract Tests

Generated types should be backed by contract checks.

Options:

schema compatibility checks
OpenAPI diff checks
consumer-driven contract tests
mock server validation
integration tests against staging

If the backend changes a required field, CI should catch it before users do.

Performance Tests

TypeScript cannot prove Core Web Vitals.

Performance needs measurement.

bundle budget checks
Lighthouse or lab checks
field monitoring
React profiling
route-level JavaScript budgets
long task analysis

Types can protect instrumentation shape, but metrics are runtime outcomes.

Common Mistakes

Common mistakes include:

assuming types replace tests
using untyped fixtures
mocking APIs with any
testing implementation details only
forgetting schema behavior tests
not running typecheck in CI
ignoring performance budgets because code compiles

Compiled does not mean correct.

Senior Trade-Offs

Use type-level tests for shared utilities where type behavior is critical.

Use runtime tests for user behavior, schemas, API compatibility, accessibility, and performance.

Do not overinvest in type-level tests for simple feature code.

The testing strategy should match risk.

Interview Framing

In an interview, say:

I treat TypeScript as one layer of quality. It catches contract mistakes early, but I still test runtime behavior, schema validation, API compatibility, accessibility, and performance outcomes.

Revision Notes

  • TypeScript and tests are complementary.
  • Typecheck should run in CI.
  • Fixtures and mocks should satisfy real types.
  • Type-level tests help shared generic utilities.
  • Runtime schemas need runtime tests.
  • Performance and accessibility require behavioral verification.

Follow-Up Questions

  • What does TypeScript catch that tests might miss?
  • What do tests catch that TypeScript cannot?
  • When would you write type-level tests?
  • Why should mocks be typed?
  • How would you test API contract compatibility?

How I Would Answer This In A Real Interview

I would say TypeScript is a quality gate, not the whole quality strategy. I run typecheck in CI, keep fixtures and mocks typed, and write type-level tests for shared utilities where the type behavior matters. I still write runtime tests for forms, schemas, user flows, accessibility, and performance budgets because those are behaviors, not just shapes.