TypeScript Interview Questions: Narrate Your Type Decisions
TypeScript interview questions reward reasoning, not recitation. Learn how to narrate type decisions, handle follow-ups, and stay honest when you are unsure.
Most TypeScript interview questions are not really about TypeScript. They are about whether you can make a decision, explain it, and change your mind when the constraints change. That is why two candidates can give the same definition of an interface and only one sounds like an engineer.
This guide is about the spoken part of a TypeScript technical interview: how to narrate type decisions, how to handle the follow-up questions that interviewers stack on top of a simple answer, and what to do when you genuinely do not know. The goal is not a script. It is a habit of reasoning out loud that holds up under pressure.
Why TypeScript Interview Questions Reward Trade-Off Thinking
A dictionary definition tells the interviewer what a word means. A reasoned answer tells them how you work.
Consider the difference. A memorised answer to "what is a union type?" is "a type that can be one of several types." A reasoned answer sounds more like this: "A union says a value is one of a known set of shapes. I use it when the set is closed and I want the compiler to force me to handle every case. If the set is open, or if the shapes share almost nothing, I would reach for something else, because every consumer has to narrow before it can do anything useful."
The second answer contains context, a constraint, and a consequence. It also hands the interviewer something to follow up on, which is exactly what they want.
A structure that works for almost any TypeScript interview question:
- State the concept in one plain sentence.
- Give a concrete example from code you have actually written or read.
- Name the trade-off you accepted by choosing it.
- Say when you would choose differently.
That fourth step is the one candidates skip, and it is the one that signals seniority. Anyone can say what a tool does. Saying when you would not use it shows judgement.
Keeping answers honest when your experience is thin
If you have read about a feature but never shipped it, say so plainly and then reason from what you do know. "I have not used template literal types in production. My understanding is that they let you build string types from other string types, which is useful for things like typed event names. I would want to check how they behave with a large union before adopting them, because I would worry about compile time."
That answer is honest, it demonstrates a mental model, and it shows you know what you would need to verify. It is far stronger than a confident bluff that collapses on the first follow-up.
Types, Interfaces and the Questions Behind the Question
When an interviewer asks about type aliases versus interfaces, they are usually testing three things: whether you know the mechanical differences, whether you can separate those differences from style preference, and whether you can pick one under a stated constraint.
The mechanical differences worth naming
- An interface can be extended and can be merged across declarations. A type alias cannot be reopened once declared.
- A type alias can name things an interface cannot, such as a union, a tuple, a mapped type, or a conditional type.
- Both can describe an object shape, and in most everyday code the two are interchangeable.
The trap is turning this into a style argument. "Interfaces are better" is not an answer, because it is not true in general. A better shape is: "For object shapes in a codebase where consumers might want to extend or augment, I default to an interface. When I need a union or a computed type, I use a type alias. I care more about consistency within a file than about winning the argument."
A worked example
Suppose you are modelling an order.
interface Order {
id: string;
lines: OrderLine[];
status: OrderStatus;
}
type OrderStatus = "draft" | "placed" | "shipped" | "cancelled";Narrate it. "The order itself is an interface because it is an object shape that other modules may want to extend, for example an audit wrapper. The status is a union of string literals, so it has to be a type alias. That union is doing real work: it makes an invalid status a compile error rather than a runtime surprise."
Now the common follow-up: "Which would you use in a shared library, and why?" A reasonable answer is that it depends on the library's contract. If consumers are expected to extend the public shapes, interfaces give them a supported way to do that. If the library mostly exports unions and utility types, aliases are unavoidable. What the interviewer wants to hear is the reasoning, not a rule.
Structural Typing and Assignability: Explaining the Mental Model
TypeScript compares types by shape, not by name. Most candidates can say this. Fewer can explain why an object literal sometimes fails to assign to a type that a variable of the same shape assigns to happily.
Start in plain language
"TypeScript does not care what you called the type. It cares whether the value has the required members with compatible types. If I have a function that expects something with a name string, I can pass anything that has one."
Then add the wrinkle. "There is one exception people trip over: when you assign a fresh object literal directly, TypeScript checks for excess properties. So passing { name: 'Ada', age: 36 } to something expecting only name is an error, because the extra property is probably a typo. If I store that object in a variable first, the check does not fire, because the variable's type has already been widened."
Reasoning aloud about an assignability error
When you hit a red squiggle in a live exercise, do not guess. Walk through it:
- Read the error and identify the target type and the source type.
- Check whether the source is a fresh object literal. If it is, excess property checking may be the cause.
- Check optional versus required members. An optional property on the target is fine; a missing required one is not.
- Check function parameter types. Parameters are compared in a way that surprises people, because a function that accepts a wider input can be used where a narrower one is expected.
- If it is still unclear, hover the types or assign to an intermediate variable to see what the compiler inferred.
Saying this sequence out loud is often more impressive than fixing the error instantly, because it shows a method you can reuse.
Widening, narrowing and inference
Expect follow-ups here. A short, accurate answer beats a long, vague one.
- Widening: a
letwith a string literal infersstring, because the variable can be reassigned. Aconstkeeps the literal type. - Narrowing: control flow analysis narrows a union inside an
ifor aswitch, so you can use the specific member without a cast. - Inference: TypeScript infers from the initialiser. If you want a wider or narrower type than it picks, annotate.
Generics, Constraints and Knowing When to Stop
A useful one-line definition: generics let you write code that works for many types while preserving the relationship between them.
The relationship is the point
If a function takes a value and returns that same type, a generic preserves the link. If it takes a value and returns unknown or any, the link is lost and the caller has to assert. That is the trade you are making.
function first<T>(items: T[]): T | undefined {
return items[0];
}Narrate it: "The generic is not decoration. It says the return type is the same as the element type, so the caller keeps type information. Without it, I would have to return any or unknown and cast at every call site."
Constraints, defaults and inference
- A constraint, written as
T extends { id: string }, says what the caller must provide, so you can use those members inside the function. - A default, written as
T = string, gives the caller a fallback when inference cannot help. - Inference means callers usually do not write the type argument at all, which is why a well-designed generic API feels ordinary to use.
When to avoid generics
Have a real example ready. "I avoid generics when there is only one type in play, or when the type parameter appears once and does not relate anything. At that point it is a placeholder that makes the signature longer without making it safer. I would rather write the concrete type and keep the function readable." That answer is a genuine trade-off, and interviewers notice it.
Narrowing, Unions and Exhaustiveness in Real Code
Discriminated unions are a modelling tool. The tag field is not bookkeeping; it is what lets the compiler help you.
type Result =
| { kind: "ok"; value: number }
| { kind: "error"; message: string };
function describe(result: Result): string {
switch (result.kind) {
case "ok":
return "Value: " + result.value;
case "error":
return "Error: " + result.message;
default: {
const never: never = result;
return never;
}
}
}The never assignment is an exhaustiveness check. If someone adds a pending variant later, the assignment fails to compile, and you find out at build time instead of in production. That is the sentence to say out loud, because it explains why the ceremony is worth it.
Guards, assertion functions and ceremony
A type guard returns a boolean and narrows at the call site. An assertion function narrows by throwing or by declaring the type outright, which is more powerful and easier to misuse. The trade-off is safety versus ceremony: assertion functions remove repeated checks but push responsibility onto the caller to get the condition right.
Strictness settings
If asked about compiler settings, keep it practical. Strict mode turns on a group of checks, including strict null checks, that catch whole categories of bug. Many teams adopt it gradually because a large existing codebase will light up with errors. A good answer names the reason for the gradual approach, not just the setting.
Handling Follow-Up Questions Under Pressure
Interviewers stack follow-ups to test depth and honesty, not memory. The second and third question are where they learn whether your first answer was understanding or recall.
A three-step response when you are unsure
- Clarify the question. "Do you mean at the type level, or how it behaves at runtime?"
- State what you know. Give the part you are confident about, and mark it as such.
- Describe how you would verify it. "I would write a small file, hover the inferred type, and check the compiler output. That is usually faster than searching."
That third step is the one that turns an uncertain moment into a competent one. It shows you have a method for closing gaps.
Buying thinking time without bluffing
Pausing is fine. Filling the pause with confident nonsense is not. Useful phrases: "Let me think about the trade-off for a second." Or: "I want to separate two things here." These are honest and they slow the conversation to a pace where you can reason.
Turning "I have not used that" into a useful answer
Do not stop at the admission. Bridge to adjacent experience. "I have not used conditional types directly, but I have written overloads and mapped types, and I understand the idea of distributing over a union. I would expect conditional types to be the tool for the same kind of problem when overloads get unwieldy." You have answered honestly and shown transferable understanding.
Practising Out Loud Before the Real Conversation
Reading answers silently does not build interview fluency. Speaking does. The interview is a spoken performance, and the only way to rehearse a spoken performance is to speak.
A simple loop you can run in twenty minutes:
- Pick one concept from your list, such as structural typing or exhaustiveness checks.
- Explain it out loud in ninety seconds, as if to an interviewer.
- Record yourself, or at least notice where you hesitated or reached for jargon.
- Rewrite the weak sentence, then explain it again.
- Move to a different concept the next day.
The point is repetition with feedback, not a perfect script. Keep the practice honest. Do not memorise answers that claim experience you do not have, and do not rehearse a persona that would fall apart in a real conversation. The version of you that gets hired is the one who can reason on their feet, including about the limits of their knowledge. You can read more about how we think about preparation on our blog, and the boundaries we set for using the product are set out in our authorized use policy.
A Short Checklist for TypeScript Interview Day
- Prepare two or three projects you can discuss in depth, including specific type decisions you made and why.
- For each decision, be ready to say "it depends" followed immediately by the factors it depends on.
- Ask a clarifying question before answering any ambiguous prompt.
- Review the basics you use daily, so you can explain them simply rather than reaching for jargon.
- Practise the four-step answer structure until it feels natural: concept, example, trade-off, when you would choose differently.
- Have one honest bridge ready for topics you have not used in production.
- Sleep, and speak at a pace that lets you think.
The takeaway is simple. TypeScript interview questions are a reasoning exercise wearing a syntax costume. If you can state a concept, show an example, name the trade-off and say when you would choose differently, you will sound like an engineer making deliberate choices, which is what the interview is actually measuring. If you want to build that fluency out loud, our support page is a good place to start.
Frequently asked questions
How deep does TypeScript knowledge need to be for a mid-level interview?
Deep enough to reason about decisions, not to recite the handbook. For a mid-level role, expect to explain object typing, unions and narrowing, generics at a practical level, and how you handle nullability. You should be able to take a small domain model and justify each type choice. You do not need to have shipped every advanced feature, but you should be able to describe how you would verify something you have not used.
What should I say if I do not know the answer to a TypeScript question?
Clarify the question, state the part you do know, and describe how you would verify the rest. For example: do you mean the type-level behaviour or the runtime behaviour? I am confident about the first part, and for the second I would write a small file and inspect the inferred type. That is honest and it shows a method, which is usually what the interviewer is checking for.
Are type aliases or interfaces better for interview answers?
Neither is better in general, and saying so is a strong answer. Interfaces can be extended and merged across declarations; type aliases can name unions, tuples and computed types. A good response states those mechanical differences, then says you default to interfaces for object shapes that others may extend, and use aliases when a union or computed type is required, valuing consistency within a file.
How do I explain generics without overcomplicating it?
Lead with the purpose: generics preserve the relationship between types so callers keep type information. Give a short example such as a function that returns the same element type it receives. Then mention constraints, defaults and inference in one sentence each, and finish with when you would avoid generics, for instance when the type parameter appears once and relates nothing.
Should I mention strict mode and compiler settings in an interview?
Mention them briefly and practically. Strict mode enables a group of checks, including strict null checks, that catch whole categories of bug. If you are asked why some teams adopt it gradually, explain that enabling it on a large existing codebase produces many errors at once, so teams often tighten settings file by file or folder by folder.
How can I practise TypeScript interview answers on my own?
Speak them out loud. Pick one concept, explain it in about ninety seconds as if to an interviewer, record yourself, note where you hesitated or reached for jargon, then fix that sentence and try again. Then choose a new concept for your next session. The loop only works if you actually say the words rather than reading them.
Practise your TypeScript answers out loud and review how they land.