Skip to main content
Guides Interview prep TypeScript Mock Interview Questions in 2026 — Practice Prompts, Answer Structure, and Scoring Rubric
Interview prep

TypeScript Mock Interview Questions in 2026 — Practice Prompts, Answer Structure, and Scoring Rubric

10 min read · April 25, 2026

Practice TypeScript interviews with realistic 2026 prompts, answer structures, scoring rubrics, strong and weak examples, and a 7-day prep plan. Use this as a mock interview guide for frontend, full-stack, and platform engineering loops.

TypeScript mock interview questions in 2026 are no longer just “What is a generic?” or “Explain interfaces versus types.” Strong engineering interviews test whether you can use TypeScript to design safer APIs, model real product states, debug inference problems, and communicate tradeoffs without hiding behind clever types. This guide gives you realistic practice prompts, answer structures, scoring rubrics, strong and weak examples, and a 7-day plan for turning TypeScript knowledge into interview performance.

TypeScript mock interview questions in 2026: what interviewers are really testing

Most TypeScript interviews evaluate four things at once:

  1. Language fundamentals: unions, intersections, narrowing, generics, mapped types, conditional types, overloads, utility types, and module boundaries.
  2. Practical modeling: representing product states, API responses, permissions, form schemas, async results, and impossible states.
  3. Debugging judgment: reading compiler errors, fixing inference issues, and knowing when a type abstraction is too clever.
  4. Engineering communication: explaining tradeoffs to teammates who may not be type-system specialists.

A great answer is not the longest type definition. It is the answer that makes invalid states hard to represent, preserves readability, and fits the team’s maintenance level.

Scoring rubric for TypeScript mock interviews

Use this rubric to score yourself after each prompt.

| Score | What it means | Observable behavior | |---:|---|---| | 1 | Struggles with basics | Cannot explain unions/generics/narrowing; writes any to escape errors | | 2 | Basic syntax, weak judgment | Knows common features but overuses assertions or misses edge cases | | 3 | Solid working level | Models data safely, explains tradeoffs, fixes common inference issues | | 4 | Senior-level | Designs maintainable types, anticipates API evolution, avoids overengineering | | 5 | Staff-level | Creates simple type patterns that improve team velocity and system correctness |

For most frontend and full-stack roles, a score of 3 is hireable if the coding and system design loops are strong. Senior roles should target 4. Platform, design-system, SDK, or developer-tools roles may require 4-5 because TypeScript becomes part of the product surface.

Answer structure that works for most prompts

Use a repeatable structure so you do not ramble.

Step 1: Restate the problem in product terms. “We need to represent a checkout state where only some states have an error, and submitted orders must have an id.”

Step 2: Name the type-system tool. “A discriminated union is a good fit because the status field can narrow the available properties.”

Step 3: Write the simplest safe version. Do not start with conditional mapped types if a union solves the problem.

Step 4: Explain edge cases. Talk about unknown API data, optional properties, exhaustive checks, runtime validation, or migration path.

Step 5: State the tradeoff. “This is slightly more verbose than one interface with optional fields, but it prevents impossible combinations.”

That structure shows judgment, not just memory.

Prompt 1: Model an async request state

Question: Design TypeScript types for an async user profile fetch. The UI has idle, loading, success, and error states. Only success should have data; only error should have error.

Strong answer:

type User = { id: string; name: string };

type ProfileState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: User }
  | { status: "error"; error: string };

function render(state: ProfileState) {
  switch (state.status) {
    case "success":
      return state.data.name;
    case "error":
      return state.error;
    default:
      return null;
  }
}

What to say: “A discriminated union prevents a state like { status: 'success', error: 'Oops' }. It also gives the UI safe narrowing.”

Weak answer:

type ProfileState = {
  status: string;
  data?: User;
  error?: string;
};

This compiles, but it allows impossible states and pushes correctness into runtime checks.

Prompt 2: Explain unknown versus any

Question: When should you use unknown instead of any?

Strong answer: any disables type checking for that value and anything derived from it. unknown means “we do not know yet,” so TypeScript forces us to narrow before use. For external input such as JSON, local storage, query params, webhooks, or postMessage data, unknown is safer.

Example:

function parseUser(value: unknown): User | null {
  if (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  ) {
    const candidate = value as { id: unknown; name: unknown };
    if (typeof candidate.id === "string" && typeof candidate.name === "string") {
      return { id: candidate.id, name: candidate.name };
    }
  }
  return null;
}

Scoring note: A senior answer mentions runtime validation libraries such as Zod, Valibot, or custom guards, but should not pretend TypeScript validates JSON at runtime.

Prompt 3: Build a generic API result type

Question: Create a reusable type for API responses that can either succeed with data or fail with an error code and message.

Strong answer:

type ApiSuccess<T> = { ok: true; data: T };
type ApiFailure<E extends string = string> = {
  ok: false;
  error: { code: E; message: string };
};

type ApiResult<T, E extends string = string> = ApiSuccess<T> | ApiFailure<E>;

Then explain: “The ok boolean is the discriminator. The generic keeps the success payload reusable, and the error code generic lets a caller restrict expected errors without making every endpoint overly complex.”

Follow-up: How would you force exhaustive handling of error codes?

A good answer might use a switch and never check:

function assertNever(value: never): never {
  throw new Error(`Unhandled case: ${value}`);
}

Then use it in a switch when the error code union is known.

Prompt 4: type versus interface

Question: When do you choose type and when do you choose interface?

Strong answer: Both can model object shapes. Interfaces are good for public object contracts, extension, and declaration merging. Type aliases are more flexible for unions, intersections, mapped types, conditional types, and primitives. In application code, consistency matters more than absolutism.

A senior answer includes tradeoff: “For a design-system public prop contract, I might use interface if extension is expected. For a discriminated union of component states, I need type. I avoid declaration merging unless it is intentional because it can surprise maintainers.”

Weak answer: “Interfaces are always better” or “types are always better.” That sounds dogmatic and misses real-world context.

Prompt 5: Create a safe event map

Question: You are building an analytics helper. Each event name should require the right payload shape.

Strong answer:

type EventMap = {
  signup_started: { source: "homepage" | "invite" };
  signup_completed: { userId: string; plan: "free" | "pro" };
  project_created: { projectId: string; template?: string };
};

function track<K extends keyof EventMap>(event: K, payload: EventMap[K]) {
  // send event
}

track("signup_completed", { userId: "u1", plan: "pro" });
// track("signup_completed", { userId: "u1" }); // compile error

What interviewers like: The answer uses a generic key and indexed access type. It improves developer experience without becoming unreadable.

Follow-up: How do you handle events loaded from config? Strong answer: “If event names come from runtime config, TypeScript cannot prove them statically. I would validate config at runtime and generate or infer types from a typed schema where possible.”

Prompt 6: Debug a bad generic

Question: Why does this function lose type safety?

function getValue(obj: object, key: string) {
  return obj[key];
}

Strong answer: object does not say which keys exist, and string is too broad. The fixed version ties the key to the object type:

function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

Then add judgment: “This is useful for typed objects, but if the key truly comes from user input, we still need runtime checking because keyof T is compile-time only.”

Prompt 7: Utility types in practical use

Question: Explain Pick, Omit, Partial, Required, and Record with realistic examples.

Strong answer:

  • Pick<User, "id" | "name"> creates a small view model.
  • Omit<User, "passwordHash"> prevents exposing sensitive fields.
  • Partial<FormState> represents incremental form updates, but should not be used for fully validated data.
  • Required<Config> can represent normalized config after defaults are applied.
  • Record<FeatureFlag, boolean> maps every known flag to a value.

Senior caveat: “Utility types can hide intent if stacked too deeply. If a type alias becomes Partial<Omit<Pick<...>>>, I would name the concept or define a clearer shape.”

Prompt 8: Runtime validation and TypeScript limits

Question: Your API returns User[]. How do you make sure it is safe?

Strong answer: “TypeScript cannot verify runtime JSON. I would treat the response as unknown, validate it, and then narrow to User[]. In a production app I would use a schema library or generated API client so the runtime and compile-time contracts stay aligned.”

A practical example:

async function fetchUsers(): Promise<User[]> {
  const response = await fetch("/api/users");
  const json: unknown = await response.json();
  const users = parseUsers(json);
  if (!users) throw new Error("Invalid users response");
  return users;
}

What not to say: “I cast it with as User[].” Assertions are sometimes necessary at boundaries, but an interview answer should show you understand the risk.

Prompt 9: React props with discriminated unions

Question: Design props for a Button that can render as a link or a real button. Link mode needs href; button mode needs onClick.

Strong answer:

type LinkButtonProps = {
  kind: "link";
  href: string;
  children: React.ReactNode;
};

type ActionButtonProps = {
  kind: "button";
  onClick: () => void;
  children: React.ReactNode;
};

type ButtonProps = LinkButtonProps | ActionButtonProps;

Then explain that the discriminant prevents callers from passing neither href nor onClick. A more advanced answer may discuss polymorphic as props, but should acknowledge that polymorphic components can get complicated quickly and may not be worth it for every codebase.

Prompt 10: Conditional types without overengineering

Question: Explain a conditional type and give a practical use.

Strong answer:

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

This uses infer to extract the resolved value. Then add a tradeoff: “Conditional types are powerful for library code and reusable helpers, but I avoid them in everyday product code unless they remove real duplication. Clever types can slow the compiler and confuse teammates.”

7-day TypeScript mock interview prep plan

Day 1: Fundamentals audit. Review unions, narrowing, generics, keyof, indexed access, utility types, and unknown. Write five small examples from memory.

Day 2: Product state modeling. Practice discriminated unions for async states, forms, permissions, checkout, and feature flags. Focus on making impossible states impossible.

Day 3: API boundaries. Practice parsing unknown, writing type guards, and explaining runtime validation. Build one small ApiResult<T> helper.

Day 4: React and frontend patterns. Type component props, event handlers, custom hooks, reducers, and design-system variants. Practice explaining where types improve user experience for developers.

Day 5: Debugging drills. Take broken generic examples and fix them. Read TypeScript error messages out loud and explain the cause before changing code.

Day 6: Mock interview. Answer five prompts under time pressure. Record yourself or write notes. Score each answer with the rubric.

Day 7: Senior polish. Prepare stories about type migrations, API contract improvements, runtime validation, reducing any, or simplifying overcomplicated types. Senior candidates should show judgment, not just syntax.

Final checklist before the interview

You are ready when you can:

  • Explain unknown versus any without sounding theoretical.
  • Use discriminated unions for UI and API states.
  • Write a generic function with K extends keyof T.
  • Discuss type versus interface with nuance.
  • Model event maps or API results safely.
  • Explain why TypeScript does not validate runtime data.
  • Know when to stop adding type complexity.
  • Communicate tradeoffs clearly while coding.

The best TypeScript interview answers in 2026 are practical. They show that you can make a codebase safer, easier to refactor, and better for teammates without turning every problem into a type puzzle. Practice the prompts, score yourself honestly, and keep the focus on maintainable correctness.