Type-Level Testing + Public API Design (Library Author Mindset)
How to keep TypeScript APIs stable: test your types, design exports intentionally, and avoid leaking internals that you can’t maintain.
On this page
What you’ll learn
- What “type-level testing” means (and why it matters)
- How to assert compile-time behavior using
@ts-expect-error - Public API rules: how not to export types you’ll regret
30‑second interview answer
Type-level tests ensure your TypeScript API behaves as intended: certain code should compile, and certain misuse should fail. You can do this with @ts-expect-error in a dedicated test file or tools like tsd. This matters for libraries because types are part of your public contract; a breaking type change can be as harmful as a runtime breaking change.
Type-level testing with @ts-expect-error
Create a file like types.test-d.ts or type-tests.ts (not shipped to runtime).
// Suppose this is your public API
export function createId(prefix: "usr" | "org"): `${"usr" | "org"}_${string}` {
return `${prefix}_${Math.random().toString(36).slice(2)}` as any;
}
const id = createId("usr");
// id should be a template literal type
// @ts-expect-error - invalid prefix
createId("post");The build should fail if the expected error disappears (meaning your types got too permissive).
Testing assignability (pragmatic style)
A simple helper pattern:
type Assert<T extends true> = T;
type IsEqual<A, B> =
(<T>() => T extends A ? 1 : 2) extends
(<T>() => T extends B ? 1 : 2) ? true : false;
type _1 = Assert<IsEqual<"a" | "b", "a" | "b">>;This is mostly for library authors—use it sparingly.
Public API design rules (high-signal)
Rule 1: don’t leak internal types
If you export an internal type, you’re committing to maintain it.
Bad:
export type InternalState = {
cache: Map<string, any>;
debug: boolean;
};Better: export minimal, stable types.
Rule 2: prefer “input/output types” over “implementation types”
Your consumers care about inputs/outputs, not internals.
Rule 3: make invalid states unrepresentable
Use unions and discriminants so consumers can’t pass nonsense.
Production rule of thumb
- Treat exported types as semver commitments.
- Add type-level tests for the most important parts of your API.
- Keep your public surface small.
Interview questions
-
Q: What is a type-level test?
- A: A compile-time assertion that a usage compiles or fails as expected.
-
Q: Why does it matter?
- A: Types are part of your public contract; regressions break consumers.
Quick recap
- Use
@ts-expect-errorto lock in behavior. - Don’t export types you can’t maintain.
- Small public surface area = long-term flexibility.
Looking for structured, offline interview prep?
Free online lessons • Portable Amazon Kindle / Paperback
Frontend Interview Guide (2025 Edition)
By Rajesh Dhiman
Comprehensive preparation covering React internals, Web Performance, CSS architecture, and system design questions.
JavaScript Interview Guide
By Rajesh Dhiman
Deep dive into the Event Loop, microtask draining, closures, prototypal inheritance, and asynchronous execution patterns.
Note: All lessons on Frontend Interview remain completely free to read online. As an Amazon Associate, we may earn from qualifying purchases.
Written by Rajesh Dhiman
Authored by Rajesh Dhiman and engineered at Eunix Tech. Focused on deep-dive frontend engineering, JavaScript runtime internals, React architecture, and web performance.