Skip to content
tezvyn:

Top 30 Intermediate TypeScript & Web APIs Interview Questions and Answers

30 intermediate multiple-choice TypeScript & Web APIs interview questions, past the definitions: how the pieces fit together, what breaks in practice, and the trade-off behind a choice. They come from 30 bites in the TypeScript & Web APIs library, the middle slice of the 125 TypeScript & Web APIs interview questions in the 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.

  1. Question 1 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?

  2. Question 2 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.

    Read the full bite: Explain the difference between any and unknown, and demonstrate type-safe narrowing

  3. Question 3 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?

  4. Question 4 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

  5. Question 5 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

  6. Question 6 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

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

  8. Question 8 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?

  9. Question 9 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?

  10. Question 10 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?

  11. Question 11 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

  12. Question 12 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?

  13. Question 13 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?

  14. Question 14 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?

  15. Question 15 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?

  16. Question 16 of 30

    Which statement accurately describes what happens when a TypeScript class uses implements with an interface?

    Show the answer

    Answer: b · The compiler checks type compatibility at build time and removes the implements clause in output

    Implements is a compile-time-only contract check that is erased during transpilation, producing no runtime code. Distractor A confuses it with extends, which actually manipulates the prototype chain, whereas implements only verifies structural shape before compilation completes.

    Read the full bite: What is the purpose of the implements keyword?

  17. Question 17 of 30

    Which technique validates at runtime that a querySelector result is specifically an HTMLInputElement?

    Show the answer

    Answer: c · Using an instanceof HTMLInputElement check before accessing input properties

    Only instanceof performs runtime type validation. The generic querySelector<HTMLInputElement> approach is purely a compile-time contract and will not prevent errors if the selector actually matches a non-input element.

    Read the full bite: Strategies to type querySelector results as HTMLInputElement

  18. Question 18 of 30

    Which sequence type-safely reads dataset.id from a clicked LI during event delegation when the LI contains nested children?

    Show the answer

    Answer: b · Cast event.target to HTMLElement, call closest('li'), verify instanceof HTMLLIElement, then read dataset.id

    Option B is correct because event.target may be a nested child, so closest walks up to the LI and instanceof narrows the type safely before reading dataset.id. Option D is tempting but wrong because casting event.target directly to HTMLLIElement is unsafe and breaks when clicks land on nested elements.

    Read the full bite: How do you type-safely check an LI click and read data-id?

  19. Question 19 of 30

    When creating a div in TypeScript and setting its plain text, which approach combines the precise DOM interface with the safest property assignment?

    Show the answer

    Answer: a · const div: HTMLDivElement = document.createElement('div'); div.textContent = 'Hello';

    TypeScript overloads createElement('div') to return HTMLDivElement, and textContent is preferred over innerHTML for plain text to avoid XSS and unnecessary parsing. Option B is tempting because innerText looks equivalent, but it triggers style recalculation and differs in behavior, while Option D defeats TypeScript's type safety by using any.

    Read the full bite: Programmatically create and append a div in TypeScript

  20. Question 20 of 30

    Inside an async function's try block you call doWork() without awaiting it, and it rejects. What happens?

    Show the answer

    Answer: c · The rejection escapes the try/catch and becomes an unhandled rejection

    try/catch only catches rejections of promises you actually await; an unawaited call's rejection escapes the block. There is no auto-retry, and TypeScript does not require await to compile.

    Read the full bite: Promise .catch() versus async/await try...catch

  21. Question 21 of 30

    When one of three parallel fetch calls rejects, how does Promise.all behave compared to Promise.allSettled?

    Show the answer

    Answer: d · Promise.all rejects immediately on the first failure and discards sibling results, while allSettled always fulfills with an array of status objects.

    Promise.all is fail-fast: it rejects immediately on the first failure and does not surface values from sibling promises, whereas allSettled always fulfills with an array of outcome objects. Option A is tempting because many candidates wrongly assume Promise.all returns partial results, but it actually discards them entirely.

    Read the full bite: How do you fetch from three APIs using Promise.all versus allSettled?

  22. Question 22 of 30

    How do you correctly annotate and implement the return of an async function that yields a User object?

    Show the answer

    Answer: a · Annotate the return type as Promise<User> and return a User value from the body

    An async function implicitly wraps the returned value in a Promise, so the annotation must be Promise<User> while the body simply returns User. Option C is wrong because omitting Promise causes a type error, and Option D is redundant because async already wraps the return value, which can create a nested Promise type.

    Read the full bite: How do you type an async function return in TypeScript?

  23. Question 23 of 30

    In an async TypeScript function fetchUser(id: number): Promise<User> that wraps fetch, what is the primary reason to check response.ok before calling response.json()?

    Show the answer

    Answer: c · fetch resolves even on 4xx or 5xx statuses, so ok is needed to detect HTTP failures before parsing.

    fetch resolves rather than rejects on HTTP error codes such as 404 or 500, so checking response.ok is the only way to detect server-side failures before parsing. Distractor A repeats the common misconception that fetch auto-rejects on 4xx/5xx, which would make the ok check unnecessary.

    Read the full bite: Write a typed async fetchUser with error handling

  24. Question 24 of 30

    Which approach correctly attaches a JWT to a fetch request while following standard security and syntax practices?

    Show the answer

    Answer: d · Provide an options object as the second argument with headers: { Authorization: 'Bearer ' + token }

    fetch requires an options object as its second argument, and the Authorization header must use the Bearer scheme followed by a space and the token. Option A is tempting if you are used to interceptor patterns from libraries like Axios, but mutating the fetch prototype creates dangerous hidden side effects.

    Read the full bite: How do you include a JWT in a fetch request?

  25. Question 25 of 30

    When an API returns HTTP 200 for both success and failure, which strategy best enforces type safety across the network boundary?

    Show the answer

    Answer: b · Treat the body as unknown, use a type guard to validate it against a discriminated union, and wrap the outcome in a Result type

    Treating the body as unknown and validating it with a type guard forces runtime checks and enables narrowing to a discriminated union, which a Result type then surfaces to callers. Casting with as is dangerous because it bypasses both runtime validation and the type checker, allowing error payloads to be treated as success data.

    Read the full bite: How do you handle an API returning 200 for success and failure?

  26. Question 26 of 30

    In a generic fetchJson wrapper, why is it critical to check response.ok before returning res.json() as Promise<T>?

    Show the answer

    Answer: d · Because fetch resolves even on 4xx/5xx statuses, so skipping the check would return an error payload incorrectly typed as T.

    fetch resolves successfully on HTTP error codes such as 404 or 500, so without the ok guard the caller would receive an error body wrongly typed as T. Option A is a tempting misconception because fetch only rejects on network failures, not on HTTP error statuses.

    Read the full bite: Create a generic fetchJson wrapper with typed response and error handling

  27. Question 27 of 30

    In getProperty<T, K extends keyof T>(obj: T, key: K), what is the effect of changing the return type from T[K] to T[keyof T]?

    Show the answer

    Answer: c · The return type widens to a union of all property types in T instead of the specific type for the provided key.

    T[K] looks up the exact type for the specific key passed, whereas T[keyof T] resolves to a union of every property type on T because keyof T is a union of all keys. The most tempting distractor is wrong because key validation still happens at the parameter level via K extends keyof T, regardless of what the return type uses.

    Read the full bite: Write a generic type-safe getProperty using keyof

  28. Question 28 of 30

    What is the main risk of declaring separate generic parameters on getState and setState instead of one class-level T?

    Show the answer

    Answer: b · The methods could be invoked with unrelated types, so getState and setState no longer agree on the state type.

    A class-level generic binds every method to the same concrete type after instantiation, but method-level generics let getState and setState operate on unrelated types, breaking state consistency. Distractor A describes a different mistake—forgetting to type the constructor argument—not the specific consequence of scattering generics across methods.

    Read the full bite: Create a generic State class with getState and setState

  29. Question 29 of 30

    What is the main type-safety benefit of making fetchJSON generic with <T> rather than having it return Promise<any>?

    Show the answer

    Answer: c · It lets the caller decide the expected response shape for compile-time checking without changing the wrapper code.

    The generic T is supplied by the caller, allowing compile-time type checking and autocomplete without hardcoding shapes in the wrapper. C is wrong because TypeScript generics are erased at compile time and never perform runtime validation.

    Read the full bite: Write a generic fetchJSON<T> wrapper and explain its type safety benefits

  30. Question 30 of 30

    Using function merge<T, U>(a: T, b: U): T & U { return { ...a, ...b }; }, what is the runtime value of a key present in both objects?

    Show the answer

    Answer: b · The value from b, since b is spread last and overwrites a's key

    Object spread applies properties left to right, so b's value overwrites a's for shared keys at runtime. The intersection type does not merge values, and overlapping keys do not cause a compile error here.

    Read the full bite: Generic merge with an intersection return type

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.

Get it on Google PlayiPhone app coming soon