TypeScript Interview Questions: Types, Generics, and Strict Mode
Reviewed by Mark Dickie · Last updated
4 questions on this page you can answer and see marked on the spot. Go to the first one
TypeScript is a typed superset of JavaScript that adds static types and compiles down to plain JavaScript. Interviewers mostly test whether you can model data with types, not whether you have memorized syntax. Expect questions on structural typing, narrowing, generics, and the compiler settings that change how strict your code is. Strong candidates explain why the compiler accepts or rejects a line, not only what the type is.
What does a TypeScript interview test?
| Area | What it tests | Example to rehearse |
|---|---|---|
| Structural typing | Compatibility depends on shape, not declared names | Why a variable holding an object with extra properties is accepted, but an object literal with extra properties is rejected |
| Narrowing | Control-flow analysis after typeof, in, instanceof, or truthiness checks | Refining a string | number parameter inside an if branch |
| Discriminated unions | Modeling states so impossible combinations do not compile | A kind field plus a switch with a never check in the default branch |
| Generics | Constraints with extends, default type parameters, and inference | A function that returns the property type of whichever key the caller passes |
| Compiler options | What strict, strictNullChecks, and noImplicitAny each change | The same code failing under strict and passing without it |
| Declaration files | Typing untyped JavaScript and package APIs | Describing a legacy JavaScript module with declare module |
Practice these before the call:
- Build a discriminated union for a fetch result (loading, success, error), then make the compiler reject a switch that misses one branch.
- Write a recursive
DeepPartial<T>mapped type and test it on a nested config object. - Type a function that takes an array of keys and returns an object built from them, using
keyofand generics. - Turn on
strictin a small project and fix every error without reaching foranyor type assertions.
Key facts
- Tarmac has 98 TypeScript interview questions on this topic, 10 of them on this page, at difficulty 2–4 of 5.
- Tarmac tracked 1,884 job postings asking for TypeScript in October 2026.
- Roles asking for TypeScript advertise a median base salary of £80,000, across 477 job postings as of October 2026.
- Tarmac last reviewed these TypeScript interview questions on 2 November 2026.
At a glance
| Questions | 10 shown · 98 in the bank |
|---|---|
| Difficulty | 2–4 of 5 |
| Formats | Multiple answer, True / false, Code output, Fill in the blank, Coding exercise, Ordering, Multiple choice, Find the bug, Short answer, Flashcard |
| Interactive | 1 run your code against tests, in the app |
What you'll review
- partial required
- primitives
- generic functions
- pick omit
- exhaustiveness
- discriminated unions
- mapped types
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
Which of these are built-in TypeScript utility types?#
Options
Pick every one that applies.
Show answer
Partial<T>, Pick<T, K>, and Readonly<T> are all built-in utility types — but Maybe<T> is not. Maybe comes from some functional-programming libraries rather than TypeScript's standard library, so the compiler does not provide it out of the box.
Partial, Pick, and Readonly are built in. Maybe is not part of TypeScript's standard library (it comes from some FP libraries).
TypeScript types are erased during compilation and have no presence in the emitted JavaScript.#
Options
Show answer
True. Type annotations, interfaces, and generics are erased during compilation and leave no trace in the emitted JavaScript. That is why you cannot inspect a TypeScript type at runtime — there is nothing left to check, so you must reach for runtime guards like typeof or a validation library instead.
Type annotations, interfaces, and generics are erased at compile time. That is why you cannot check a type at runtime — there is nothing left to inspect (you must use runtime guards like typeof or a validation library).
What does this log?#
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
console.log(first([10, 20, 30]));Show answer
10
The generic first infers T = number from the argument and returns arr[0], which is 10. Types are erased at runtime; the behaviour is just plain array indexing.
_____<User, 'id' | 'name'> keeps only those two keys, while _____<User, 'password'> keeps everything except that one.#
Show answer
**Pick**<User, 'id' | 'name'> keeps only those two keys, while **Omit**<User, 'password'> keeps everything except that one.
Pick selects a subset of keys; Omit removes them. They're complements — Omit<T, K> is Pick<T, Exclude<keyof T, K>>.
Implement pluck(items, key): given an array of objects and a key, return the array of values at that key. Type it generically so key is constrained to keyof T and the return type is T[K][].#
Starter code
function pluck(items: unknown[], key: string): unknown[] {
// TODO: generic signature + implementation
return [];
}Your solution must pass
- plucks names
This one is written and run, not read. Solve it in the app and your code is executed against these tests and the hidden ones.
You're handling a Shape discriminated union exhaustively. Order the steps that make the compiler enforce exhaustiveness.#
Put these in order
Show answer
To make the compiler enforce exhaustiveness, follow these steps in order:
switch (shape.kind)on the discriminant- Handle each known
kindin its owncase - Assign the value to a
neverindefault
If a new variant is later added without a case, the default assignment to never becomes a compile error.
Switch on the discriminant, handle each variant, then in default assign the narrowed value to a const _exhaustive: never — if a new variant is added later, that assignment becomes a compile error.
Which of the following mapped types is exactly equivalent to TypeScript's built-in Partial<T> utility type?#
Options
Show answer
type MyPartial<T> = { [P in keyof T]?: T[P] }; is exactly equivalent to Partial<T>. It maps over every key of T and applies the ? modifier, making each property optional while preserving its original type. Without the ?, the properties stay required; using -? would strip optionality (like Required<T>); and T[P] | undefined alone keeps properties required but unioned with undefined, which is not the same as optional.
TypeScript's built-in Partial<T> makes all properties optional by mapping over the keys with { [P in keyof T]?: T[P] }. The question asks candidates to reconstruct it manually. The mapped type { [P in keyof T]?: T[P] } is correct: it maps every key to an optional property of the same type. The mapped type { [P in keyof T]: T[P] } forgets the ? modifier (that's Readonly<T>-style without readonly). The mapped type { [P in keyof T]-?: T[P] } uses -? which REMOVES optionality (that's Required<T>). The mapped type { [P in keyof T]: T[P] | undefined } maps to T[P] | undefined without the ? modifier, which makes every property required but explicitly typed as T[P] | undefined — subtly different from optional.
The following DeepPartial<T> utility type is intended to recursively make all properties at every nesting level optional. It has exactly one bug. Identify the buggy line.#
type DeepPartial<T> =
T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: never;Show answer
The bug is on line 4.
The code defines DeepPartial<T> which recursively makes all nested properties optional. On line 2, the conditional check should be T extends object (not T extends Object — though that works too, but the real bug is elsewhere). Actually the bug is on line 3: the recursive mapped type uses T[P] instead of DeepPartial<T[P]> for the value — wait, let's read carefully. Line 3 is [P in keyof T]?: DeepPartial<T[P]> — that looks correct. The actual bug is on line 2: T extends object ? { ... } : T — for non-object primitives this is fine. The real bug in this snippet is on line 4: the else branch returns never instead of T. When T is a primitive like string or number, DeepPartial<T> should return T itself, not never. So never on line 4 is the buggy line.
What is a discriminated union and why is it useful?#
Show answer
A discriminated (tagged) union is a union of object types that share a common literal property — the discriminant. Switching on that property lets TypeScript narrow the value to one exact member, so you can handle each case type-safely and get exhaustiveness checking for free.
The shared literal field (e.g. type: 'circle' | 'square') is what lets the compiler narrow inside a switch. Adding a never default then enforces that every member is handled.
For type T = { a: 1; b: 2 }, what does keyof T produce?#
Show answer
'a' | 'b' — a union of T's property keys (as string literal types).
keyof turns an object type's keys into a union of literal types. Combined with generics (<K extends keyof T>) it powers type-safe property access like Pick and get(obj, key).
Sources
The official documentation these questions are checked against:
Related interview questions
Job market
See typescript salaries and hiring demand from live job postings.
The other 88 questions
This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 88 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 answers per 30 days, each marked with written feedback