TypeScript MCQs multiple-choice questions with answers & explanations
All 28 TypeScript quiz questions on one page. Pick an answer in your head, then open Show answer to check it and read why. Want a score and a timer? Take them as a quiz instead.
- 1.easy
With
stricton, which line fails to compile?let a: any = 'hello'; let u: unknown = 'hello'; a.toUpperCase(); // A u.toUpperCase(); // B if (typeof u === 'string') u.toUpperCase(); // C- AOnly A
- BOnly B
- CB and C
- DNone of them
Show answer
Answer: B (Only B)
anyswitches checking off, so A compiles (and would crash at runtime ifawere a number).unknownaccepts any value but allows no operations until you narrow it, so B fails with'u' is of type 'unknown'. Thetypeofcheck in C narrowsutostring, so it compiles. - 2.easy
What does the compiler do with these declarations?
interface User { name: string } interface User { age: number } type Point = { x: number }; type Point = { y: number };- ABoth pairs merge
- BBoth pairs are Duplicate identifier errors
- C
Usermerges to have bothnameandage;Pointis a Duplicate identifier error - D
Useris a Duplicate identifier error;Pointmerges to have bothxandy
Show answer
Answer: C (
Usermerges to have bothnameandage;Pointis a Duplicate identifier error)Interfaces are open: declarations with the same name in the same scope merge their members, which is how libraries let you augment types like
Window. Type aliases are closed, so declaringPointtwice is error TS2300, Duplicate identifier. To combine type aliases, use an intersection. - 3.easy
What is
ColorName?const colors = { red: '#f00', green: '#0f0' }; type ColorName = keyof typeof colors;- A
'red' | 'green' - B
string - C
'#f00' | '#0f0' - D
{ red: string; green: string }
Show answer
Answer: A (
'red' | 'green')typeof colorsturns the value into its type,{ red: string; green: string }, andkeyoftakes the union of its keys. For the values you would write(typeof colors)[keyof typeof colors], which is juststringhere unless you addas const. - A
- 4.easy
Given
type User = { id: number; name: string; email: string }, which type is exactly{ id: number; name: string }?- A
Omit<User, 'id' | 'name'> - B
Partial<User> - C
Pick<User, 'id' | 'name'> - D
Extract<User, 'id' | 'name'>
Show answer
Answer: C (
Pick<User, 'id' | 'name'>)Pick<T, K>keeps only the listed keys.Omit<User, 'id' | 'name'>does the opposite and leaves{ email: string }.Partialkeeps every key but makes them optional, andExtractfilters union members, so here it producesnever. - A
- 5.easy
What types does TypeScript infer for
a,bandc.greeting?let a = 'hello'; const b = 'hello'; const c = { greeting: 'hello' };- A
'hello','hello','hello' - B
string,string,string - C
string,'hello','hello' - D
string,'hello',string
Show answer
Answer: D (
string,'hello',string)A
constprimitive can never change, so it keeps the literal type'hello'. Aletcan be reassigned, so it widens tostring. Object properties are mutable too, soc.greetingwidens tostringeven thoughcisconst. Useas constto keep the literals. - A
- 6.easy
What is
Exclude<'a' | 'b' | 'c', 'a' | 'c'>?- A
'a' | 'c' - B
'b' - C
never - D
'a' | 'b' | 'c'
Show answer
Answer: B (
'b')Exclude<T, U>removes every member of the unionTthat is assignable toU. It is the distributive conditional typeT extends U ? never : Tapplied to each member, and the resultingnevers vanish from the union, leaving'b'.Extractkeeps the matching members instead. - A
- 7.easy
Which call is a compile error?
function total(nums: readonly number[]) { return nums.length; } function append(nums: number[]) { nums.push(0); } const mutable = [1, 2]; const frozen: readonly number[] = [1, 2]; total(mutable); // 1 total(frozen); // 2 append(frozen); // 3- AOnly 1
- BOnly 3
- C2 and 3
- DNone of them
Show answer
Answer: B (Only 3)
A mutable
number[]can go where areadonly number[]is expected, because the function only promises not to mutate it. The reverse is unsafe:appendcouldpushinto an array the caller declared read-only, so call 3 errors. Typing parameters asreadonlylets a function accept both kinds. - 8.mid
What is
Role?const roles = ['admin', 'editor'] as const; type Role = (typeof roles)[number];- A
string - B
string[] - C
readonly ['admin', 'editor'] - D
'admin' | 'editor'
Show answer
Answer: D (
'admin' | 'editor')as constturns the array into a readonly tuple of literal types, and indexing it withnumbergives the union of its element types. Withoutas const,roleswould bestring[]andRolejuststring. The pattern gives you a runtime list and a matching type from one source. - A
- 9.mid
Why does
items.lengthfail to compile understrict?function count(items: string[] | null) { if (typeof items === 'object') { return items.length; } return 0; }- A
typeofcannot narrow arrays - B
typeof nullis'object', soitemsis stillstring[] | nullinside theif - C
lengthdoes not exist onstring[] - DIt compiles fine
Show answer
Answer: B (
typeof nullis'object', soitemsis stillstring[] | nullinside theif)Because
typeof null === 'object'at runtime, TypeScript keepsnullin the narrowed type and reports'items' is possibly 'null'. Useif (items),items !== nullorArray.isArray(items)instead. - A
- 10.mid
The
trianglevariant was just added toShape. Where does the compiler report an error?type Shape = | { kind: 'circle'; r: number } | { kind: 'square'; size: number } | { kind: 'triangle'; base: number; height: number }; function area(s: Shape): number { switch (s.kind) { case 'circle': return Math.PI * s.r ** 2; case 'square': return s.size ** 2; default: { const unreachable: never = s; return unreachable; } } }- ANowhere;
areareturnsundefinedfor triangles at runtime - BOn
const unreachable: never = s - COn
switch (s.kind) - DOn the return type, because not all code paths return a value
Show answer
Answer: B (On
const unreachable: never = s)In the
defaultbranch,sis narrowed to whatever the cases did not handle. Before the change that wasnever; now it is the triangle type, which is not assignable tonever, so the assignment errors. This exhaustiveness check turns a forgotten variant into a compile error. - ANowhere;
- 11.mid
Which assignment is a compile error?
interface Options { verbose: boolean } const a: Options = { verbose: true, silent: true }; // 1 const extra = { verbose: true, silent: true }; const b: Options = extra; // 2- ABoth
- BNeither
- COnly 2
- DOnly 1
Show answer
Answer: D (Only 1)
Excess property checking applies only to fresh object literals assigned directly to a typed target, because an unknown key there is most likely a typo.
extrais not fresh, and under structural typing it has everythingOptionsneeds, so assigning it is fine. - 12.mid
Which line fails to compile?
type Config = Record<string, string | number>; const annotated: Config = { port: 3000 }; const checked = { port: 3000 } satisfies Config; annotated.port.toFixed(); // A checked.port.toFixed(); // B- AOnly B
- BOnly A
- CBoth
- DNeither
Show answer
Answer: B (Only A)
An annotation widens the variable to
Config, soannotated.portisstring | number, which has notoFixed.satisfieschecks the object againstConfigbut keeps its inferred type, sochecked.portis stillnumber. - 13.mid
What does this log?
enum Direction { Up, Down } console.log(Direction[0], Object.keys(Direction).length);- A
Up 2 - B
undefined 2 - C
Up 4 - D
0 4
Show answer
Answer: C (
Up 4)Numeric enums compile to an object with a reverse mapping,
{ 0: 'Up', 1: 'Down', Up: 0, Down: 1 }. SoDirection[0]is'Up'and the object has 4 keys. String enum members get no reverse mapping. - A
- 14.mid
What is the inferred type of
age?function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { name: 'Ada', age: 36 }; const age = getProp(user, 'age');- A
number - B
string | number - C
unknown - D
any
Show answer
Answer: A (
number)Kis inferred as the literal'age', and the return typeT[K]is an indexed access type, so it resolves tonumber. ReturningT[keyof T]instead would givestring | number. The constraint also makesgetProp(user, 'email')a compile error. - A
- 15.mid
With
strictenabled, which assignment is an error?class Animal { name = '' } class Dog extends Animal { bark() {} } let handleAnimal = (a: Animal) => {}; let handleDog = (d: Dog) => {}; handleDog = handleAnimal; // 1 handleAnimal = handleDog; // 2- ANeither
- BOnly 1
- COnly 2
- DBoth
Show answer
Answer: C (Only 2)
Under
strictFunctionTypes, function parameters are contravariant. A function that accepts anyAnimalcan safely stand in for one that only ever receivesDogs, so 1 is fine. ADoghandler used as anAnimalhandler could be called with aCatand try tobark(), so 2 errors. - 16.mid
What does TypeScript say about
parse(value)?function parse(input: string): number; function parse(input: number): string; function parse(input: string | number): string | number { return typeof input === 'string' ? Number(input) : String(input); } const value = Math.random() > 0.5 ? 'a' : 1; const x = parse(value);- AError: no overload matches, because
string | numberfits neither signature - B
xisstring | number - C
xisnumber - D
xisany
Show answer
Answer: A (Error: no overload matches, because
string | numberfits neither signature)Callers see only the overload signatures, never the implementation signature, and each overload is tried on its own. A
string | numberargument matches neither, so the call fails with No overload matches this call. Add a third overload for the union, or use one signature (or a generic with a conditional return type) instead. - AError: no overload matches, because
- 17.mid
Which declaration is a compile error?
type A = { name?: string }; type B = { name: string | undefined }; const a: A = {}; const b: B = {};- ANeither
- BOnly
a - CBoth
- DOnly
b
Show answer
Answer: D (Only
b)name?:means the key may be missing entirely.name: string | undefinedmeans the key must be present, even if its value isundefined, so{}is missingname. WithexactOptionalPropertyTypes,name?: stringadditionally stops you from assigning an explicitundefined. - 18.mid
With
stricton, which lines are compile errors?const a: {} = 42; // 1 const b: object = 42; // 2 const c: Object = 'hi'; // 3 const d: {} = null; // 4- A2 and 4
- BOnly 2
- C1, 2 and 4
- DAll four
Show answer
Answer: A (2 and 4)
{}means any non-nullish value, primitives included, so 1 compiles.objectmeans non-primitive, so 2 errors.Objectbehaves much like{}and accepts'hi'. UnderstrictNullChecks,nullis not assignable to{}, so 4 errors. UseobjectorRecord<string, unknown>when you really mean an object. - 19.mid
With
strict: trueand no other flags, what is the type ofs?const scores: Record<string, number> = { ada: 10 }; const s = scores.grace;- A
number - B
number | undefined - C
undefined - D
unknown
Show answer
Answer: A (
number)strictdoes not includenoUncheckedIndexedAccess. Without that flag, reading any key of aRecord<string, number>is typednumber, even thoughsisundefinedat runtime. Turning the flag on makes index readsnumber | undefined, which forces a check. - A
- 20.mid
How many members does the union
ClassNamehave?type Size = 'sm' | 'lg'; type Color = 'red' | 'blue'; type ClassName = `${Size}-${Color}`;- A1, because it widens to
string - B2
- C8
- D4
Show answer
Answer: D (4)
Template literal types distribute over the unions in every placeholder and produce the cross product:
'sm-red' | 'sm-blue' | 'lg-red' | 'lg-blue'. This grows quickly with more placeholders, and TypeScript refuses to build unions that get too large. - A1, because it widens to
- 21.mid
Compiled with
target: es2022, what does this log at runtime?class Account { private pin = 1234; #secret = 'x'; } const acc = new Account(); console.log((acc as any).pin, Object.keys(acc));- A
undefined [] - B
1234 ['pin', '#secret'] - C
1234 ['pin'] - DIt throws a
TypeError
Show answer
Answer: C (
1234 ['pin'])privateis only a compile-time check. The emitted JavaScript has an ordinarypinfield, so casting toanyreads it andObject.keyslists it.#secretis a native private field enforced by the engine: it never appears inObject.keysand cannot be read from outside the class at all. - A
- 22.hard
What is
Result?type ToArray<T> = T extends unknown ? T[] : never; type Result = ToArray<string | number>;- A
(string | number)[] - B
never - C
unknown[] - D
string[] | number[]
Show answer
Answer: D (
string[] | number[])A conditional type whose checked type is a naked type parameter distributes over unions: it is evaluated as
ToArray<string> | ToArray<number>, givingstring[] | number[]. To get(string | number)[], turn distribution off by wrapping both sides in tuples:[T] extends [unknown] ? T[] : never. - A
- 23.hard
Under
strict, which line is a compile error?class Animal { name = '' } class Dog extends Animal { bark() {} } interface WithMethod { run(a: Animal): void } interface WithProp { run: (a: Animal) => void } const dogOnly = (d: Dog) => d.bark(); const m: WithMethod = { run: dogOnly }; // 1 const p: WithProp = { run: dogOnly }; // 2- AOnly 1
- BOnly 2
- CBoth
- DNeither
Show answer
Answer: B (Only 2)
strictFunctionTypesonly applies to function-type syntax likerun: (a: Animal) => void. Parameters declared with method shorthand are still checked bivariantly, a deliberate exception that keeps types likeArray<T>covariant. So 1 is accepted (unsoundly) and 2 errors. Prefer property syntax when you want strict checking. - 24.hard
What is
Result?type Mutable<T> = { -readonly [K in keyof T]-?: T[K] }; type Frozen = { readonly id: number; readonly note?: string }; type Result = Mutable<Frozen>;- A
{ readonly id: number; readonly note?: string } - B
{ id: number; note: string } - C
{ id: number; note?: string } - D
{ id: number; note: string | undefined }
Show answer
Answer: B (
{ id: number; note: string })-readonlystrips thereadonlymodifier and-?strips optionality from every key. Removing?also removes theundefinedthat optionality added, sonotebecomes plainstring, the same thing the built-inRequired<T>does. - A
- 25.hard
What are
AandB?type Unwrap<T> = T extends Promise<infer U> ? U : T; type A = Unwrap<Promise<string[]>>; type B = Unwrap<number>;- A
Aisstring[],Bisnumber - B
Aisstring[],Bisnever - C
AisPromise<string[]>,Bisnumber - D
Aisstring,Bisnumber
Show answer
Answer: A (
Aisstring[],Bisnumber)infer Ucaptures whatever type sits insidePromise<...>when the match succeeds, soAisstring[]; the array is not unwrapped.numberdoes not match, so the false branch returnsTitself. The built-inAwaited<T>does this recursively for nested promises and thenables. - A
- 26.hard
In TypeScript 5.5 or later, what is the type of
defined?const values = [1, undefined, 2, undefined]; const defined = values.filter((v) => v !== undefined);- A
(number | undefined)[] - B
undefined[] - C
number[] - D
any[]
Show answer
Answer: C (
number[])Since TypeScript 5.5 the compiler infers type predicates from simple function bodies, so
(v) => v !== undefinedis treated as(v) => v is numberand the narrowing overload offilterapplies. Before 5.5 the result was(number | undefined)[]and you needed an explicit(v): v is number => ...guard. - A
- 27.hard
Which line is a compile error?
type Callback = () => void; const cb: Callback = () => 42; // 1 function run(): void { return 42; // 2 }- ABoth
- BNeither
- COnly 1
- DOnly 2
Show answer
Answer: D (Only 2)
A function type returning
voidmeans the caller will ignore the result, so a function that returns something can still be assigned to it; that is what lets you passarr.pushtoforEach. A function declaration annotated: voidmust not return a value, so 2 errors. - 28.hard
What is
Keys?type Cat = { name: string; meow(): void }; type Dog = { name: string; bark(): void }; type Keys = keyof (Cat | Dog);- A
'name' - B
'name' | 'meow' | 'bark' - C
never - D
string
Show answer
Answer: A (
'name')A value of type
Cat | Dogis only guaranteed to have the keys both members share, sokeyofof a union gives the common keys:'name'. Intersections work the other way round:keyof (Cat & Dog)is'name' | 'meow' | 'bark'. - A