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?

AreaWhat it testsExample to rehearse
Structural typingCompatibility depends on shape, not declared namesWhy a variable holding an object with extra properties is accepted, but an object literal with extra properties is rejected
NarrowingControl-flow analysis after typeof, in, instanceof, or truthiness checksRefining a string | number parameter inside an if branch
Discriminated unionsModeling states so impossible combinations do not compileA kind field plus a switch with a never check in the default branch
GenericsConstraints with extends, default type parameters, and inferenceA function that returns the property type of whichever key the caller passes
Compiler optionsWhat strict, strictNullChecks, and noImplicitAny each changeThe same code failing under strict and passing without it
Declaration filesTyping untyped JavaScript and package APIsDescribing a legacy JavaScript module with declare module

Practice these before the call:

  1. Build a discriminated union for a fetch result (loading, success, error), then make the compiler reject a switch that misses one branch.
  2. Write a recursive DeepPartial<T> mapped type and test it on a nested config object.
  3. Type a function that takes an array of keys and returns an object built from them, using keyof and generics.
  4. Turn on strict in a small project and fix every error without reaching for any or 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

Questions10 shown · 98 in the bank
Difficulty2–4 of 5
FormatsMultiple answer, True / false, Code output, Fill in the blank, Coding exercise, Ordering, Multiple choice, Find the bug, Short answer, Flashcard
Interactive1 run your code against tests, in the app

What you'll review

  1. partial required
  2. primitives
  3. generic functions
  4. pick omit
  5. exhaustiveness
  6. discriminated unions
  7. 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.

Why:

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.

Why:

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
Why:

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.

Why:

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:

  1. switch (shape.kind) on the discriminant
  2. Handle each known kind in its own case
  3. Assign the value to a never in default

If a new variant is later added without a case, the default assignment to never becomes a compile error.

Why:

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.

Why:

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.

Why:

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.

Why:

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).

Why:

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.

Start with this topic

Free · the whole bank · 100 answers per 30 days, each marked with written feedback

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.