TypeScript / Node.js — Theory
Single-threaded but non-blocking — how
Section titled “Single-threaded but non-blocking — how”JS on one thread. Blocking ops (I/O, crypto, DNS) → libuv. Network = OS async (epoll/kqueue/IOCP). File + DNS = thread pool. CPU-bound work blocks every request → use worker threads, child processes, or offload.
Event loop trick questions
Section titled “Event loop trick questions”setImmediatevssetTimeout(fn, 0): from main script = nondeterministic. From I/O callback =setImmediatealways first.nextTickvs Promise:nextTickbefore Promises. Both between phases.- Poll phase: blocks waiting for I/O if no timers/setImmediate queued.
Memory
Section titled “Memory”- V8: young (scavenge) + old (mark-sweep + compact). Generational GC.
- Default cap ~1.5GB on 64-bit. Raise:
--max-old-space-size=4096. - Leaks: unbounded caches, listener leaks, closures holding refs, timers never cleared.
- Diagnose:
--inspect, Chrome DevTools heap snapshot,clinic.js,--heapsnapshot-signal=SIGUSR2.
Concurrency primitives
Section titled “Concurrency primitives”- Cluster: master forks N workers per CPU; share port via OS. Each worker own V8. Stateless HTTP.
- Worker threads: own V8 isolate, message-passing or
SharedArrayBuffer. CPU-bound work. - Child process: full new Node process. Heaviest.
TypeScript senior questions
Section titled “TypeScript senior questions”unknownvsany:unknownforces narrow. Use incatch(e: unknown)(TS 4.4+).- Discriminated union: literal tag
kind. Switch narrows. Better than class hierarchy. - Generic constraint:
<T extends { id: string }>enforces shape. infer:type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T.- Mapped modifiers:
+?/-?,+readonly/-readonly,asrename keys. - Variance: params contravariant (with
strictFunctionTypes), return covariant.
Common interview Qs
Section titled “Common interview Qs”- Walk
app.get('/x')lifecycle: TCP accept → llhttp → router → middleware → response → flush. - Why
awaitinforserializes — parallelize withPromise.all. - Debug event loop lag:
monitorEventLoopDelay,perf_hooks, clinic doctor. child_process.forkvsworker_threads.Worker— process boundary vs thread/isolate.- Promise rejection in Node 15+ — exits by default.
- Type
pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>. - Graceful shutdown: drain in-flight, close DB/Redis, exit.
Pitfalls
Section titled “Pitfalls”- CJS/ESM interop fragility.
- Arrow
thislexical vs regular dynamic. JSON.stringifyon circular ref throws.- Race on async init — share single
init()Promise. - Pre-Node 15 unhandled rejections silently lost.