FreeExam
A closed notebook, a blank card, and a pencil on a wooden desk.

JavaScript and TypeScript

20 cards

Runtime behavior, asynchronous failures, object identity, and the limits of static types at application boundaries.

Card 1 of 20

Front. Microtask Starvation. Why can repeatedly scheduling resolved-promise callbacks prevent a loading spinner from painting?

These cards are original study material. They are not questions from a named employer or a live hiring loop.

All 20 cards in this deck

  1. 1.Microtask Starvation

    Why can repeatedly scheduling resolved-promise callbacks prevent a loading spinner from painting?

    The browser drains microtasks before the next rendering opportunity. A self-replenishing microtask queue can monopolize execution; yield through an appropriate task or move CPU work off the main thread.

  2. 2.Await Render

    Does await Promise.resolve() guarantee that the browser paints before execution resumes?

    No. It resumes through the microtask queue. Use an execution strategy that actually permits rendering, and verify behavior with a performance trace.

  3. 3.Promise All

    One request in Promise.all rejects. What happens to the other requests?

    The aggregate rejects, but other operations are not automatically cancelled. Propagate abort signals where supported and decide whether their results or side effects still matter.

  4. 4.Async Foreach

    Why does await items.forEach(async item => save(item)) not await all saves?

    forEach discards callback return values and returns undefined. Use a sequential for-of loop or explicitly await collected promises, with a concurrency bound for large lists.

  5. 5.Fetch Status

    Why can fetch resolve successfully for an HTTP 500 response?

    Network completion and HTTP success are different. Check response.ok or the expected status before parsing the success payload, and handle error response shapes separately.

  6. 6.Stale Closure

    A callback created when count is 2 runs after count becomes 5. Why can it still use 2?

    It closes over the binding or render environment from its creation. Identify which value should be captured and which must be read at invocation; do not assume callbacks automatically refresh.

  7. 7.Shallow Copy

    Why does changing copy.user.name also change original.user.name after copy = {...original}?

    Spread copies only the outer object. Both objects reference the same nested user; copy the path being changed or use a suitable immutable update strategy.

  8. 8.Const Object

    Does const settings = {theme: 'dark'} prevent settings.theme from changing?

    No. const prevents reassigning the binding. Runtime immutability requires a separate discipline or mechanism; Object.freeze itself is shallow.

  9. 9.Nullish Default

    Why is value ?? fallback safer than a logical-OR default for a valid quantity of zero?

    Nullish coalescing replaces only null or undefined. Logical OR also replaces false, zero, and an empty string, potentially discarding valid input.

  10. 10.Number ID

    Why should a 64-bit database identifier often cross a JSON API as a string?

    JavaScript numbers cannot exactly represent all integers beyond 2^53 - 1. Parsing a large numeric ID can silently change its value; preserve it as a string or use an explicit BigInt encoding contract.

  11. 11.JSON Loss

    Why is JSON.parse(JSON.stringify(value)) not a general deep-clone operation?

    Serialization changes or loses values such as undefined and Date objects, rejects cycles and ordinary BigInt values, and does not preserve arbitrary object types. Choose a clone operation matching the actual data model.

  12. 12.Unknown Boundary

    Why should unvalidated JSON enter application code as unknown rather than a trusted interface?

    Static annotations do not inspect runtime data. Validate structure and constraints before narrowing it to the domain type; an assertion alone cannot establish the contract.

  13. 13.Assertion

    What runtime protection does response as User provide?

    None. It tells the compiler to treat the value as User. Malformed fields remain malformed at runtime and need validation at the boundary.

  14. 14.Discriminant

    How can a discriminated union prevent accessing data before a request succeeds?

    Give each state a distinct literal tag and define data only on the success variant. Checking the tag narrows the allowed fields and makes invalid state combinations harder to express.

  15. 15.Exhaustive

    How can never help detect an unhandled new state in a switch?

    Pass the default branch's value to a function accepting never. With an exhaustive discriminated union, adding a variant makes that branch's type non-never and produces a compiler error.

  16. 16.Readonly Limit

    Does TypeScript readonly prevent another alias from mutating an object at runtime?

    No. It restricts assignments through that typed reference and is erased at runtime. Avoid mutable aliases or enforce runtime immutability when the invariant depends on it.

  17. 17.Array Index

    Why can an array lookup return undefined even when its element type is string?

    The index may be outside the array or refer to a hole. Check bounds or absence, and consider noUncheckedIndexedAccess so types expose that possibility.

  18. 18.Satisfies

    When is satisfies preferable to a broad type annotation for a configuration literal?

    It checks compatibility while generally retaining the expression's more specific inferred shape. It is compile-time checking, not runtime validation or an exact-object guarantee for arbitrary inputs.

  19. 19.Structural Types

    Why can a variable with extra fields be passed to a function expecting a smaller object type?

    TypeScript largely uses structural compatibility. Excess-property checks for fresh literals are not a universal ban on extra runtime fields; validate or select fields when exactness matters.

  20. 20.Async Error

    Why does try { return failingPromise(); } catch {} fail to catch a later rejection inside that function?

    The rejection happens after synchronous control leaves the try. Await inside the try or attach a rejection handler; ensure the caller observes the returned promise as well.