Top 30 TypeScript & Web APIs Interview Questions and Answers
30 multiple-choice questions on TypeScript & Web APIs, of the kind that come up in a technical interview, drawn from 30 bites in the TypeScript & Web APIs library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.
TypeScript, browser APIs, WebAssembly, PWAs
30 questions. Pick an answer, or open “Show the answer” to read it.
Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.
Question 1 of 30
Which TypeScript snippet correctly declares a string name and a numeric array using proper primitive and array type annotations?
Show the answer
Answer: a · const userName: string = "Alice"; const luckyNumbers: number[] = [7, 13, 21]
TypeScript requires lowercase string and number for primitive type annotations, and number[] correctly types an array of numbers. Option B is wrong because uppercase String and Number refer to rare built-in wrapper object types rather than the primitive value types used for standard annotations.
Read the full bite: Declare a string name and an array of lucky numbers in TypeScript
Question 2 of 30
When you write let message = 'hello' without a type annotation, what is the resulting compile-time behavior?
Show the answer
Answer: a · TypeScript infers string and later rejects message = 42 with a type error
TypeScript analyzes the right-hand side to infer string, so reassigning a number later causes a compile-time error. Distractor A is wrong because omitting an annotation does not default to any; the compiler deduces a specific static type instead.
Question 3 of 30
What happens when TypeScript cannot infer a type and noImplicitAny is disabled?
Show the answer
Answer: c · The compiler silently assigns the any type, disabling further static analysis for that value
When noImplicitAny is disabled, TypeScript silently falls back to any for uninferrable values, which opts them out of static checking and allows runtime errors to go undetected. Option B describes what happens when the flag is enabled, making it a tempting reversal.
Read the full bite: What is noImplicitAny and why is it best practice?
Question 4 of 30
Which operation is valid on a variable of type unknown without first narrowing its type?
Show the answer
Answer: b · Assigning a string value to the unknown variable
Any value can be assigned to an unknown variable, but you cannot read properties, call methods, or assign it to a narrower type without narrowing first. Options A and D confuse unknown with any, which is assignable to all types, while B requires narrowing because the compiler cannot guarantee the method exists.
Question 5 of 30
When handling a parameter typed as string or string array, which approach correctly narrows the value at runtime?
Show the answer
Answer: d · Check whether Array.isArray(arg) is true
Array.isArray is the recognized type guard that safely narrows the union within the block. Checking typeof against array is a runtime bug because typeof returns object for arrays, never array.
Read the full bite: How do you type and guard a string-or-string-array argument?
Question 6 of 30
When publishing a TypeScript library, why might you export an interface instead of a type alias for a config object?
Show the answer
Answer: a · Interfaces allow consumers to safely augment the shape via declaration merging.
Interfaces support declaration merging, allowing consumers to safely augment public library types, whereas type aliases are closed. Option D is wrong because type aliases can absolutely describe objects with methods and properties; they are not limited to primitives or unions.
Read the full bite: Key differences: type alias vs interface for object shapes
Question 7 of 30
Which getProperty signature both rejects invalid keys at compile time and returns the exact type of the requested property?
Show the answer
Answer: c · Two generics T and K where K extends keyof T and the return type is T[K]
Option C is correct because constraining K with extends keyof T limits keys to valid properties on T, while T[K] preserves the exact type of the accessed property. Option D is tempting because keyof T does restrict keys, but T[keyof T] produces a union of all property types rather than the specific one for the key passed.
Read the full bite: Create a generic getProperty using generics and keyof
Question 8 of 30
What makes a user-defined type guard unsound when its predicate claims value is User but the body only verifies value is not null?
Show the answer
Answer: d · TypeScript narrows the type in conditional branches while the value may still lack required User properties at runtime
A type predicate is purely a compile-time hint; if the runtime check is too permissive, TypeScript narrows the type based on a false promise, allowing unsafe property access. Distractor B is wrong because TypeScript never mutates or coerces runtime values to satisfy a type predicate.
Read the full bite: What is a type predicate? Write a custom type guard for User.
Question 9 of 30
During a normal page navigation in the same browser tab, which object is replaced while the other persists?
Show the answer
Answer: b · Only the document object is replaced; the window object persists as the tab container.
The window object represents the browser tab and global execution context, so it persists across navigation, while the document is the in-memory DOM representation of a specific page and is rebuilt. Claiming both are replaced is a common misconception that treats window and document as the same page-level entity rather than as container versus content.
Read the full bite: What is the fundamental difference between window and document?
Question 10 of 30
In a browser script, what distinguishes a top-level var declaration from let or const?
Show the answer
Answer: b · var creates a property on the global Window object, while let and const do not, though all three are globally scoped
var creates a writable property on the global Window object, while let and const are globally scoped without becoming Window properties. Option A is a tempting misconception because let and const are indeed globally scoped; they simply do not pollute the global object.
Read the full bite: What happens when you declare var globally versus let or const?
Question 11 of 30
Which accurately describes the return values of getElementById and getElementsByClassName?
Show the answer
Answer: c · The first returns an Element or null; the second returns a live HTMLCollection.
getElementById returns a single Element or null, while getElementsByClassName returns a live HTMLCollection that automatically reflects DOM mutations. Option D is tempting because both methods query the DOM, but getElementById never returns a collection.
Read the full bite: How do you select by ID and class, and what is returned?
Question 12 of 30
When initializing UI handlers on a page with external images, which distinction between DOMContentLoaded and window.load matters most?
Show the answer
Answer: d · DOMContentLoaded fires after deferred scripts run but does not wait for images, letting handlers bind earlier
DOMContentLoaded fires after HTML parsing and deferred scripts complete, so you can bind UI handlers before heavy assets like images finish, whereas window.load waits for all subresources. The most tempting distractor reverses the two events: window.load is the one that waits for every asset, not DOMContentLoaded.
Read the full bite: Describe the difference between DOMContentLoaded and window.load
Question 13 of 30
Which TypeScript approach correctly handles clicks on a ul containing li elements with nested child elements?
Show the answer
Answer: b · Add one click listener to the ul, use (event.target as HTMLElement).closest('li') to find the list item, and guard against null.
Option B correctly leverages event delegation and safely walks up from nested elements via closest('li'), guarding against clicks on the ul padding. Option D is tempting but fails when event.target is a nested span rather than the li itself.
Read the full bite: Describe event delegation and implement a single ul click handler in TypeScript
Question 14 of 30
You add a click listener to a parent with addEventListener('click', fn, true). When does fn run relative to a child element's default listener if the child is clicked?
Show the answer
Answer: c · fn runs before the child listener during event capture
Passing true as the third argument registers the parent listener for the capture phase, which fires as the event travels downward from the root, so it executes before the child's bubble-phase listener. Option B describes the default behavior when the third argument is omitted or false, not capture.
Read the full bite: Explain DOM event capturing and bubbling with addEventListener
Question 15 of 30
After storing parent.childNodes in a variable, a developer appends a new child and sees the stored length increase without reassignment. What explains this?
Show the answer
Answer: d · The stored reference is a live NodeList, but querySelectorAll returns a static snapshot that would not update.
childNodes is a live NodeList that mutates in place, while querySelectorAll returns a static snapshot that never updates. A is tempting because many developers assume all NodeLists share the same binding behavior, but querySelectorAll specifically captures a point-in-time copy.
Read the full bite: What are the key differences between NodeList and HTMLCollection?
Question 16 of 30
When passing a large ArrayBuffer from the main thread to a dedicated Web Worker for image processing, which technique avoids duplicating that memory in the browser?
Show the answer
Answer: c · Include the ArrayBuffer in the postMessage transfer list so ownership moves to the worker
Listing the ArrayBuffer in the transfer list moves ownership to the worker without copying, preventing memory duplication. The most tempting distractor is wrong because structured clone always copies data; postMessage never shares memory by reference between threads.
Read the full bite: Offload CPU-intensive work to a Web Worker and explain communication.
Question 17 of 30
Which statement accurately compares open and closed shadow modes regarding encapsulation and event propagation?
Show the answer
Answer: b · Closed mode returns null for external shadowRoot access but does not prevent events from bubbling out retargeted to the host
Closed mode returns null for element.shadowRoot to prevent accidental access, yet bubbling events still cross the boundary with their target retargeted to the host. Distractor D is tempting because closed sounds like a security boundary, but the platform explicitly does not protect against malicious inspection.
Read the full bite: Explain Shadow DOM, encapsulation, and event propagation
Question 18 of 30
When defining a User object shape for an external API that third-party modules may need to augment, why is an interface conventionally preferred over a type alias?
Show the answer
Answer: c · Interfaces support declaration merging, so multiple modules can safely contribute to the same User definition.
Interfaces are preferred because they support declaration merging, which lets separate files or plugins augment the User shape without conflicting definitions. The runtime performance claim is a common misconception because both constructs are fully erased during compilation and have zero runtime cost.
Read the full bite: Define a User object shape: interface vs type alias?
Question 19 of 30
You define a function return type as Product[] | { message: string }. What is the critical next step before using the returned value?
Show the answer
Answer: d · Use a type guard such as Array.isArray to narrow the union
Using a type guard like Array.isArray lets TypeScript narrow the union inside each branch, giving exact types and autocomplete. Simply returning the union without narrowing forces consumers to deal with both shapes manually, a subtler error that hides bugs until runtime.
Read the full bite: How do you type a function with two possible response shapes?
Question 20 of 30
Which is the strongest reason to prefer a string enum over a numeric enum for a status field returned by an external API?
Show the answer
Answer: a · String enums use human-readable runtime values that match the API wire format exactly, avoiding an extra mapping layer.
String enums let you assign API response values directly because their runtime values are the exact strings on the wire, unlike numeric enums which produce opaque integers and require a separate mapping. The most tempting distractor claims string enums offer reverse mapping, but that is actually a feature of numeric enums and is usually irrelevant for external API contracts.
Read the full bite: How would you use an enum to represent API statuses?
Question 21 of 30
When modeling WebSocket messages as a discriminated union, why is it critical that the kind property uses a literal type like chat instead of string?
Show the answer
Answer: c · Literal types enable the compiler to narrow the union automatically during control flow analysis
The compiler uses the literal value of kind to distinguish structurally between union members and narrow types within each branch; using plain string would make every member look identical and break narrowing. Confusing literal types with readonly is common, but readonly only controls reassignment and does not help the compiler tell variants apart.
Read the full bite: How would you model a WebSocket message discriminated union in TypeScript?
Question 22 of 30
Which TypeScript structure should you choose to make an ApiResponse<T> that cannot simultaneously hold both data and error properties?
Show the answer
Answer: d · A union of two interfaces sharing a status literal, one generic branch with data: T and the other with error: { code; message; }
A discriminated union with a shared literal status makes it impossible to represent both states at once and enables automatic type narrowing. Option A is a common mistake because optional fields and a broad string status allow objects where both data and error are present, absent, or mismatched.
Read the full bite: Write a generic ApiResponse<T> type with success and error states
Question 23 of 30
What type does a property become when two intersected TypeScript types declare it with incompatible types, such as string versus number?
Show the answer
Answer: a · It becomes never, since no value can satisfy both constraints at once
Intersecting incompatible property types produces never because a value must satisfy both declarations simultaneously. This is not a union, nor is it a declaration-level error; TypeScript allows the intersection but flags impossible assignments.
Read the full bite: How would you combine BaseRecord and PostContent into a Post type?
Question 24 of 30
Which typing strategy correctly distinguishes a single GeoJSON Coordinate from a LineString in TypeScript?
Show the answer
Answer: d · type Coordinate = [number, number]; type LineString = Coordinate[];
A tuple [number, number] enforces exactly two elements and preserves positional semantics for longitude and latitude, while Coordinate[] correctly builds a LineString from those fixed points. Option B is tempting but wrong because number[] allows any length, letting invalid coordinates like [1] or [1, 2, 3] pass the type checker.
Read the full bite: How do you type a GeoJSON Coordinate tuple and LineString array?
Question 25 of 30
Which approach is the idiomatic TypeScript solution for constructing a typesafe ApiRoute type that includes both /api/v1/<resource> collection paths and /api/v1/<resource>/{id} item paths from a finite Resource union?
Show the answer
Answer: b · A base route template interpolating Resource into /api/v1/, unioned with the same base route suffixed by /{id}
Template literal types automatically distribute a union through an interpolated position into every concrete string permutation, so unioning BaseRoute with BaseRoute/{id} is the idiomatic, machinery-free solution. Option A is tempting because it yields the same members, but it unnecessarily uses a mapped type when direct interpolation already expands the union, and C sacrifices compile-time exhaustiveness for false flexibility.
Read the full bite: Build a typesafe ApiRoute type using template literal types
Question 26 of 30
What return type does fetchItem<T extends boolean>(..., includeHistory: T): T extends true ? ExtendedItem : BaseItem yield when includeHistory is a plain boolean variable?
Show the answer
Answer: c · The type resolves to ExtendedItem | BaseItem because boolean encompasses both true and false
Because boolean is equivalent to true | false, the distributive conditional type evaluates both branches and produces a union. Option B is tempting but wrong because TypeScript does not narrow a plain boolean variable to a literal across a function boundary.
Read the full bite: How would you model fetchItem's return type with generics and conditional types?
Question 27 of 30
You need a type alias for a function that takes a string and an optional number parameter defaulting to 5. Which is valid?
Show the answer
Answer: b · type F = (s: string, n?: number) => boolean
Function type expressions can mark parameters as optional with ? but cannot encode default values, which are implementation-only details erased at compile time. Option A is invalid because default values belong in the function body, not the type annotation.
Read the full bite: Type alias for a function with optional param and default value
Question 28 of 30
When defining a TypeScript class, which outcome occurs when you write constructor(public name: string) instead of declaring a field and assigning it manually?
Show the answer
Answer: d · TypeScript automatically declares a name property on the class and initializes it from the argument
Prefixing a constructor parameter with an access modifier tells TypeScript to generate the property declaration and assignment automatically, removing the need for manual boilerplate. The distractor about still writing name: string reflects the common misconception that parameter properties require explicit field declarations, which they do not.
Read the full bite: Create a Person class using TypeScript parameter properties
Question 29 of 30
You write process(string):string and process(number):number overloads. What happens if the implementation signature uses x: string instead of x: string | number?
Show the answer
Answer: a · TypeScript reports an error because the implementation does not cover the number overload
TypeScript requires the implementation signature to cover every overload, so using only string produces a compile error because the number case is unhandled. Distractor A is wrong because narrowing the implementation does not remove the number overload from the public API; it simply makes the implementation fail to type-check.
Read the full bite: How do you type a function with string and number overloads?
Question 30 of 30
Which of the following correctly fixes lost this context in a class callback?
Show the answer
Answer: b · Defining the method as an arrow function property or binding it in the constructor
The card identifies an arrow function class field and explicit constructor binding as the two modern fixes. Option C is tempting but wrong because arrow functions do not have their own this binding and cannot be rebound.
Read the full bite: Why does this lose context in class callbacks and two TypeScript fixes?
Could you explain these out loud?
That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.