TypeScript interview questions and answers, junior to senior
These are the TypeScript questions that come up in real frontend and full-stack interviews, with short answers you can say out loud in under a minute and a few lines of code where code is clearer than words. They are grouped by level: junior questions check that you know the type system exists at compile time only, mid-level ones check narrowing and generics, senior ones check that you can design types other people use. Every answer was checked against the official TypeScript documentation and the TypeScript 6.0 release notes in October 2026.
How interviewers use TypeScript questions
Few interviewers read a list like this one aloud. More often a TypeScript question comes up in the middle of something else: you type a function in a live-coding round and the interviewer asks why you wrote unknown instead of any, or a code review exercise has an as cast that hides a bug. A good answer names the concept, shows a two-line example and says when you would not use it. The live coding guide covers how to talk while you type; the code review interview guide covers the review format.
Junior TypeScript interview questions
1. What does the TypeScript compiler actually do?
It type-checks your code and emits JavaScript with the types removed. Types do not exist at runtime: TypeScript will not validate a JSON response for you, and a type assertion does not change the value. If data comes from outside the program, you still need a runtime check or a schema validator.
2. What is the difference between type and interface?
In most cases they are interchangeable. The handbook names the key difference: a type alias cannot be re-opened to add properties, while an interface can, through declaration merging. Interfaces describe object shapes only; a type alias can also name a union, a primitive, a tuple or a mapped type. The handbook's own heuristic is to use interface until you need a feature of type (Everyday Types).
interface User { id: string }
interface User { name: string } // merged: User has id and name
type Status = 'active' | 'banned'; // a union — only a type alias can do this3. What is the difference between any, unknown and never?
any switches type checking off for that value: anything is allowed, nothing is checked. unknown also accepts any value, but you must narrow it before using it, so it is the safe type for input you do not control. never is the type with no values: the return type of a function that always throws, and what is left after every case of a union has been handled.
function parse(raw: unknown) {
if (typeof raw === 'string') return raw.trim(); // ok: narrowed to string
return raw.trim(); // error: 'raw' is of type 'unknown'
}4. What does strict turn on, and is it on by default?
It enables the strict family of checks, including strictNullChecks, noImplicitAny, strictFunctionTypes, strictPropertyInitialization and useUnknownInCatchVariables (TSConfig reference). Since TypeScript 6.0, strict is true by default; projects that relied on the old default of false now have to set "strict": false explicitly (TypeScript 6.0 release notes). Not part of strict but worth naming: noUncheckedIndexedAccess, which adds undefined to index access results.
5. What are union and intersection types?
A union A | B is a value that is one of several types; you can only use what all members share until you narrow it. An intersection A & B is a value that has everything from both. Unions model alternatives (a result that is either data or an error); intersections combine shapes (a base type plus extra fields).
6. How do null, undefined, ? and ! work under strict null checks?
With strictNullChecks on, null and undefined are separate types and you must check for them before use. name?: string means the property may be missing. The postfix ! tells the compiler a value is not null or undefined without checking; the handbook warns that it does not change runtime behaviour, so use it only when you know it is true. Optional chaining a?.b and ?? are usually better than !.
7. When should you write a type annotation, and when should you rely on inference?
Let TypeScript infer local variables and return values of small functions. Annotate function parameters (they cannot be inferred), public API boundaries such as exported functions, and places where inference would be too wide, for example an empty array that starts as never[] or an object literal you want checked against a contract.
8. Enums or a union of string literals?
Enums are real objects that exist at runtime; numeric enums also get a reverse mapping, string enums do not (Enums). A union of literals, or an object with as const, has no runtime cost beyond the object itself and stays plain JavaScript. The handbook notes that in modern TypeScript you may not need an enum when an as const object suffices. Enums also count as non-erasable syntax, so they fail under erasableSyntaxOnly and in Node.js type stripping.
const Role = { Admin: 'admin', User: 'user' } as const;
type Role = typeof Role[keyof typeof Role]; // 'admin' | 'user'Mid-level TypeScript interview questions
9. What is narrowing, and which checks narrow a type?
Narrowing is the compiler refining a wide type to a more specific one inside a branch, based on a runtime check. The checks that narrow are typeof, instanceof, the in operator, equality and truthiness checks, assignments, and user-defined type guards (Narrowing).
10. What is a discriminated union, and how do you make a switch exhaustive?
A union whose members share a literal property, the discriminant, such as kind. Checking the discriminant narrows to one member. Assigning the leftover value to never in the default branch turns a forgotten case into a compile error.
type Shape = { kind: 'circle'; r: number } | { kind: 'square'; side: number };
function area(s: Shape) {
switch (s.kind) {
case 'circle': return Math.PI * s.r ** 2;
case 'square': return s.side ** 2;
default: { const unreachable: never = s; return unreachable; }
}
}11. What is a user-defined type guard, and what is an assertion function?
A type guard is a function whose return type is a type predicate, value is T; when it returns true, the caller's variable is narrowed. An assertion function has the return type asserts value is T and throws instead of returning false. Both are only as correct as the check inside them: the compiler trusts the predicate.
function isUser(x: unknown): x is { id: string } {
return typeof x === 'object' && x !== null && 'id' in x && typeof x.id === 'string';
}12. What are generics, and what does a constraint do?
Generics let a function or type work over many types while keeping the relationship between input and output. A constraint, T extends Something, limits what T can be so you can use its members (Generics).
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProp({ id: 1, name: 'Ann' }, 'name'); // string
getProp({ id: 1 }, 'email'); // error: not a key13. Which utility types should you know?
The ones that come up most: Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract, NonNullable, ReturnType, Parameters and Awaited (Utility Types). A strong answer adds that most of them are mapped or conditional types you could write yourself.
14. What do keyof, typeof and indexed access types do?
keyof T is the union of T's property names. In a type position, typeof value takes the type of an existing value. T[K] looks up the type of a property. Together they derive types from code you already have instead of writing them twice, as in the as const example in question 8.
15. What is the difference between an annotation, as and satisfies?
An annotation, const x: T = ..., checks the value against T and makes the variable T, so you lose the specific type. as T is an assertion: it tells the compiler to trust you and checks very little. satisfies T, added in TypeScript 4.9, checks that the expression matches T without changing the inferred type (TypeScript 4.9 release notes).
type RGB = [number, number, number];
const palette = {
red: [255, 0, 0],
green: '#00ff00',
} satisfies Record<string, string | RGB>;
palette.green.toUpperCase(); // ok: still known to be a string16. What does as const do?
It makes the compiler infer the narrowest type: literal types instead of string or number, readonly properties, and readonly tuples instead of arrays. It is how you get a literal union out of a config object without an enum.
17. What is structural typing, and what is an excess property check?
TypeScript compares types by shape, not by name: any object with the required properties is assignable, even if it was never declared as that type. The exception is a fresh object literal assigned directly to a typed target: extra properties there are an error, because they are usually typos. The same object passed through a variable is not checked for extras.
18. When do you use function overloads instead of a union parameter?
When the return type depends on which argument type was passed and a single signature cannot express it. If the return type is the same either way, a union parameter is simpler and gives better error messages. Generics with conditional types are the third option and often replace overloads.
Senior TypeScript interview questions
19. How do conditional types and infer work?
T extends U ? X : Y picks a type based on assignability. Inside the condition, infer declares a type variable the compiler fills in from the match. When T is a naked type parameter and receives a union, the condition is applied to each member separately; wrapping it in a tuple, [T] extends [U], turns that distribution off (Conditional Types).
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
type A = ElementOf<string[]>; // string
type B = ElementOf<number[] | Date[]>; // number | Date (distributed)20. What are mapped types, and what does key remapping do?
A mapped type builds a new object type by iterating over keys: { [K in keyof T]: ... }. Modifiers add or remove readonly and ?. An as clause renames keys or filters them out by mapping to never (Mapped Types).
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type G = Getters<{ name: string }>; // { getName: () => string }21. What are template literal types good for?
Typing string formats: event names such as `on${string}`, CSS units, route patterns, or the getter names above. Combined with infer they can parse a string type, for example pull the parameter names out of a route like '/users/:id' (Template Literal Types).
22. What is declaration merging, and how do you augment a third-party module?
Declarations with the same name are merged: two interfaces become one, and a namespace can merge with a function, class or enum (Declaration Merging). Module augmentation uses the same mechanism to add fields to a library's types, for example a user property on a framework's request object. Type aliases do not merge.
// types/express.d.ts
import 'express';
declare module 'express' {
interface Request { userId?: string }
}23. What does strictFunctionTypes change?
It checks function parameters contravariantly: a callback that accepts only Dog cannot be passed where a callback receiving any Animal is expected, because it would be called with a cat. The check applies to function type syntax, not to method syntax, which stays bivariant for compatibility. That is why handle: (e: Event) => void is stricter than handle(e: Event): void in an interface.
24. How do isolatedModules, verbatimModuleSyntax and erasableSyntaxOnly relate to modern tooling?
Many builds no longer use tsc to emit JavaScript: bundlers and runtimes transpile one file at a time and simply strip types. isolatedModules flags code that cannot be compiled that way. verbatimModuleSyntax makes imports explicit: anything without the type modifier is kept, so type-only imports must say import type. erasableSyntaxOnly forbids syntax with runtime semantics, such as enums, namespaces with code and parameter properties, which is exactly what Node.js cannot run with its built-in type stripping (TSConfig reference). A senior answer also mentions where the compiler is going: TypeScript 6.0 is a transition release that deprecates old options ahead of TypeScript 7.0, the native port of the compiler.
How to practise these answers
- Say each answer out loud with a timer. Under a minute, one example, one "when I would not use it". If it takes longer, the interviewer stops listening.
- Type the code, do not just read it. Paste the snippets into the TypeScript Playground and break them: remove a case from the switch, pass a wrong key. Seeing the real error message is what makes the answer stick.
- Expect TypeScript inside a coding round. Most TypeScript questions arrive as follow-ups to code you just wrote. The LeetCode-in-a-real-interview guide and the technical interview preparation plan show how those rounds run.
- Prepare the stories too. "Tell me about a type error that reached production" is a behavioral question in disguise; the behavioral questions guide covers how to structure it.
Where Interview Copilot fits
We make Interview Copilot, a desktop app that listens to the call and puts a suggested answer on your screen, so this paragraph is about our own product. In a knowledge round like this one it can bring up the concept behind a question while you are answering. It does not replace knowing the material, and it belongs only in interviews where your own tools are allowed: if you are not sure what the rules are, ask the recruiter before the round starts.
Sources
- TypeScript Handbook — Everyday Types (type aliases vs interfaces, any, strictNullChecks)
- TypeScript Handbook — Narrowing
- TypeScript Handbook — Generics
- TypeScript Handbook — Conditional Types
- TypeScript Handbook — Mapped Types
- TypeScript Handbook — Template Literal Types
- TypeScript Handbook — Utility Types
- TypeScript Handbook — Enums
- TypeScript Handbook — Declaration Merging
- TSConfig Reference (strict, noUncheckedIndexedAccess, isolatedModules, verbatimModuleSyntax, erasableSyntaxOnly)
- TypeScript 4.9 release notes — the satisfies operator
- TypeScript 6.0 release notes — new defaults and deprecations
Documentation was read on 27 October 2026. Defaults change between TypeScript versions, so check the version your team uses.
FAQ
What are the most common TypeScript interview questions?
The difference between type and interface, any versus unknown versus never, how narrowing works, generics with constraints, utility types such as Partial, Pick and Omit, enums versus string literal unions, and what strict mode turns on. Senior interviews add conditional and mapped types, declaration merging and module settings.
Should I use type or interface in TypeScript?
They are interchangeable in most cases. The TypeScript handbook says the key difference is that an interface can be re-opened and merged while a type alias cannot, and suggests using interface until you need a feature only type has, such as a union or a mapped type.
Is strict mode on by default in TypeScript?
Yes, since TypeScript 6.0 the strict option defaults to true. Before 6.0 it defaulted to false. Projects that depended on the old default now need to set "strict": false explicitly.
What is the difference between unknown and any?
Both accept any value. any also turns off checking for everything you do with the value, while unknown forces you to narrow it, for example with typeof or a type guard, before you can use it. unknown is the safe choice for input you do not control.
What does the satisfies operator do?
Added in TypeScript 4.9, satisfies checks that an expression matches a type without changing the type the compiler infers for it. You keep the specific type, such as a property known to be a string, while still getting an error for typos or missing keys.
How should I prepare for a senior TypeScript interview?
Be ready to design types rather than just use them: write a generic helper with a constraint, a discriminated union with an exhaustive switch, a mapped type with key remapping, and explain the tsconfig choices of a real project, including isolatedModules and verbatimModuleSyntax for modern build tools.