Skip to content

Boarding pass · TypeScript Advanced

TypeScript · Advanced interview

An AI mock interview for senior TypeScript engineers. Reason about conditional and inferred types, template-literal and recursive types, variance and soundness, and the ecosystem features behind library-grade typings.

20–30 minEstimated6 questionsTailored live12 model answersTo study
  • Voice interview
  • Live feedback
  • Browser-only
  • No installation required

Your report · sample

AI Readiness Score

/100
Content score English score

Finish the mock and get this report — scored on content and English, with concrete tips and a model answer for every question, plus a study plan.

Your flight path

Five steps to interview-ready.

From a quick warm-up to a personalised study plan — here's the whole loop, and what you get at each gate.

  1. Step 01

    Preparation

    Skim the topics and warm up — you're briefed before takeoff.

  2. Step 02

    AI Interview

    A realistic AI interviewer asks tailored questions — answer by voice or text.

  3. Step 03

    Instant Score

    Finish and get a 0–100 readiness score across content and English.

  4. Step 04

    AI Feedback

    Concrete tips and a rewritten model answer for every response.

  5. Step 05

    Study Plan

    A personalised plan targets your weakest areas — so the next run scores higher.

Preview the AI demo

See one answer scored, end to end.

The whole loop on sample data — question, your answer, instant feedback and a study plan. Hover to pause.

aevrofy.com/session · live demoPlaying

Interviewer · Question 1

“Tell me about yourself.”

Who it's for

  • Senior engineers writing library-grade types
  • Devs who design type-safe APIs
  • Anyone targeting senior/lead TS roles
  • Engineers who debug gnarly type errors

What you'll practice

  • Conditional types and infer
  • Template-literal and recursive types
  • Variance, soundness and branded types
  • Declaration merging and type performance

Topics covered

What this level expects.

Conditional & inferred

  • Conditional types
  • infer
  • Distributivity

Mapped & template

  • Template-literal types
  • Key remapping
  • Recursive types

Variance & soundness

  • Co/contravariance
  • Unsound spots
  • Branded types

Ecosystem

  • Declaration merging
  • Module augmentation
  • Type performance

Practice questions

12 questions, with model answers.

Read them, then run the live mock to answer out loud and get scored. Tap any question to reveal a model answer.

01How do conditional types and the infer keyword work?

A conditional type chooses between two types based on an assignability test: T extends U ? X : Y. infer introduces a type variable captured from the matched position, letting you extract a piece of a type — for example, pulling the resolved value out of a Promise or the return type of a function. Together they're how utilities like ReturnType and Awaited are built.

type Unwrap<T> = T extends Promise<infer U> ? U : T;
02What are distributive conditional types?

When the checked type of a conditional is a naked type parameter and you pass a union, the conditional distributes over each member and unions the results. So T extends any ? T[] : never applied to A | B gives A[] | B[], not (A | B)[]. You can switch distribution off by wrapping the parameter in a tuple ([T] extends [U]), which is a common trick when you don't want it.

03How would you implement a utility like Awaited or DeepReadonly?

Both use recursion with conditional/mapped types. Awaited recursively unwraps nested promises with infer until the type is no longer a thenable. DeepReadonly maps each property, recursing into object types and leaving primitives alone, adding readonly at each level. The key skills are recursion, infer, and a base case to stop.

type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;
04How do template-literal types work and what are they good for?

Template-literal types build string literal types from other types, e.g. `on${Capitalize<Event>}`. Combined with unions they expand combinatorially, and with key remapping in mapped types they can derive property names. They're used for typed event names, route params, CSS-in-JS keys, and turning string patterns into precise types.

type EventName<T extends string> = `on${Capitalize<T>}`; // "click" -> "onClick"
05What can you do with key remapping in mapped types?

The as clause in a mapped type lets you rename or filter keys while mapping. You can prefix keys, build getter names with template literals, or drop keys by mapping them to never. It's how you transform one object type's shape into another — for example, generating a set of getX methods from a properties type.

type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
06What are recursive types and what are their limits?

A type can reference itself to model nested structures — JSON, trees, deep utility types. TypeScript supports recursion but enforces a recursion-depth limit to keep checking finite, and tail-recursive conditional types are optimized to allow deeper chains (useful for things like parsing tuples). Push too far and you hit 'type instantiation is excessively deep' errors, which usually means simplifying the type.

07Explain covariance and contravariance in TypeScript.

Covariance means a type relationship is preserved — an array of Dog is assignable where an array of Animal is expected. Contravariance reverses it — a function accepting Animal is assignable where one accepting Dog is expected, because it can handle more. Function parameters are contravariant under strictFunctionTypes; return types are covariant. Getting this right is what makes higher-order function types safe.

08Where is TypeScript intentionally unsound, and why?

Several places trade soundness for pragmatism: any disables checking; type assertions bypass it; method parameters are bivariant (not strictly contravariant) for ergonomics; array access doesn't account for out-of-bounds (returns the element type, not T | undefined, unless noUncheckedIndexedAccess); and structural typing lets unrelated same-shaped types interchange. These choices make TypeScript practical for real JavaScript at the cost of catching every error.

09What are branded (nominal) types and when would you use them?

Branding fakes nominal typing in a structural system by intersecting a type with a unique marker so two otherwise-identical types (e.g. UserId and OrderId, both strings) aren't interchangeable. You construct them through a validating function and the brand prevents mixing them up. It's valuable for ids, validated/sanitized strings, and units, where structural equality would be a bug.

type UserId = string & { readonly __brand: "UserId" };
10What are declaration merging and module augmentation?

Declaration merging lets multiple declarations with the same name combine — two interfaces with the same name merge their members, which is why interfaces are 'open'. Module augmentation uses declare module to add to an existing module's or global's types — for example, extending Express's Request with a user property, or adding a property to the global Window. It's how you safely type third-party extension points.

11What makes type-checking slow, and how do you fix it?

Large unions, deep recursion, heavy conditional/mapped type computation, and big inferred types blow up checker work and editor responsiveness. Fixes: annotate return types on exported functions so the checker doesn't re-infer them everywhere, break giant types up, avoid unnecessary generic depth, use project references / incremental builds, and profile with extendedDiagnostics or --generateTrace. Simpler types are usually both faster and more maintainable.

12How do you decide when a type is too clever?

When the type is harder to read than the code it guards, produces inscrutable errors, or slows the build, the cleverness is costing more than it saves. Library boundaries can justify advanced types because they protect many callers; application code usually shouldn't. The judgment call is whether the extra safety is worth the maintenance and error-message cost — often a simpler type plus a runtime check is better.

Preparation tips

Walk in ready.

  • Be able to implement a small utility type from scratch
  • Explain why a conditional type distributes over a union
  • Have a concrete branded-type use case ready
  • Know what makes type-checking slow and how to fix it

Ready for the real thing?

Run a 6-question TypeScript mock interview, answer out loud, and get a readiness score with tips and model answers.

More advanced interviews

Aevrofy · TypeScript advanced interview prep