JavaScript and TypeScript fundamentals get tested in frontend interviews in ways generalist SWE interviews skip. Closures, prototypal inheritance, the event loop, and TypeScript’s type system come up in coding rounds, in code-reading rounds, and in design discussions. Senior candidates fluent in these score well; candidates who learned React without learning the underlying language are caught fast.
This piece covers the JS and TS fundamentals senior frontend interviewers actually test, with concrete examples and what scores well.
JavaScript: closures and lexical scope
The classic question: what does this print?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3
Strong answer: var is function-scoped, so all three callbacks close over the same i. By the time the timeouts fire, the loop has finished and i is 3. With let or const, each iteration creates a new binding and the output would be 0, 1, 2.
The pattern shows up in many forms. Senior interviewers test variations: callback chains, event handlers attached in loops, closures referencing stale state.
JavaScript: this binding
Four rules for what this refers to:
- Default binding. In a regular function call,
thisis the global object (or undefined in strict mode). - Implicit binding. When called as a method (
obj.fn()),thisis the object. - Explicit binding.
fn.call(obj),fn.apply(obj), orfn.bind(obj)setthisdirectly. - new binding.
new fn()creates a new object and setsthisto it.
Arrow functions don’t have their own this; they inherit from the enclosing lexical scope. This is why arrow functions are popular for callbacks — they avoid the this-rebinding problem of regular functions.
Senior interviewers test edge cases. What is this in a callback passed to map? What about in a method extracted to a variable? What changes with strict mode?
JavaScript: prototypal inheritance
Every object has a prototype. Property access walks the prototype chain. Class syntax in modern JavaScript is sugar over prototypal inheritance — under the hood it’s still Object.create + Object.setPrototypeOf.
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
speak() { return `${this.name} barks`; }
}
const d = new Dog('Rex');
d.speak(); // 'Rex barks'
Senior interviewers test:
- How
extendsworks under the hood. - Difference between
__proto__(the actual prototype link) andprototype(a property on constructors). - When prototype-based code runs faster than class syntax (rare but exists).
- Polluting the prototype: monkey-patching
Array.prototypeis dangerous.
JavaScript: the event loop
JavaScript runs on a single thread with an event loop. Tasks come from:
- The call stack. Synchronous code.
- The microtask queue. Promises, queueMicrotask. Drained after each task; before the next render.
- The macrotask queue. setTimeout, setInterval, I/O callbacks, message events. One per loop iteration.
- requestAnimationFrame queue. Right before paint.
Standard interview question: what’s the order?
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// Output: 1, 4, 3, 2
1 and 4 are synchronous. 3 is a microtask, drained before any macrotask. 2 is a macrotask, drained after microtasks.
JavaScript: promises and async/await
Senior interviews probe edge cases:
- What does
Promise.alldo if one rejects? (Rejects immediately with that reason; other promises still settle but their results are ignored.) - What’s
Promise.allSettledvsPromise.all? (allSettled waits for all and returns an array of {status, value or reason}.) - How does
awaitwork with non-Promises? (Wraps inPromise.resolve; you can await any value.) - What’s the difference between sync errors thrown in async functions vs Promise rejections? (Sync throws in async functions become Promise rejections automatically.)
- How do you cancel a Promise? (You can’t directly; use AbortController and have the Promise check it.)
TypeScript: advanced types
Senior+ frontend interviews probe TypeScript’s type system. Topics:
Conditional types
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<number[]>; // true
type B = IsArray<number>; // false
Mapped types
type Optional<T> = { [K in keyof T]?: T[K] };
type ReadOnly<T> = { readonly [K in keyof T]: T[K] };
Template literal types
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'
Type predicates and narrowing
function isString(x: unknown): x is string {
return typeof x === 'string';
}
function process(x: unknown) {
if (isString(x)) {
x.toUpperCase(); // x is narrowed to string
}
}
Generics with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
TypeScript: utility types
The standard utility types every senior frontend candidate knows:
Partial<T>,Required<T>,Readonly<T>— modify all properties.Pick<T, K>,Omit<T, K>— select or exclude properties by key.Record<K, V>— object type with keys K and values V.ReturnType<F>,Parameters<F>— extract from function types.Awaited<T>— unwrap Promise types.NonNullable<T>— exclude null and undefined.
Common interview questions
“Implement debounce”
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
let timeoutId: number;
return ((...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
}) as T;
}
Senior signal: handles multiple arguments, properly types the function, considers edge cases (cancellation, immediate-mode option).
“Implement throttle”
function throttle<T extends (...args: any[]) => void>(fn: T, delay: number): T {
let lastCall = 0;
return ((...args) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
fn(...args);
}
}) as T;
}
“Implement deepEqual”
function deepEqual(a: unknown, b: unknown): boolean {
if (a === b) return true;
if (a == null || b == null) return false;
if (typeof a !== 'object' || typeof b !== 'object') return false;
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(k => deepEqual((a as any)[k], (b as any)[k]));
}
Senior signal: handles nulls, type-checks before recursing, doesn’t compare keys naively.
“Implement Promise.all from scratch”
function promiseAll<T>(promises: Promise<T>[]): Promise<T[]> {
return new Promise((resolve, reject) => {
const results: T[] = [];
let completed = 0;
if (promises.length === 0) { resolve([]); return; }
promises.forEach((p, i) => {
Promise.resolve(p).then(value => {
results[i] = value;
completed++;
if (completed === promises.length) resolve(results);
}).catch(reject);
});
});
}
What scores well
- Knowing the event loop ordering cold.
- Articulating the four
this-binding rules with examples. - Reading prototype-chain code without confusion.
- Writing TypeScript that uses generics correctly.
- Implementing common utility functions (debounce, throttle, deepEqual) under time pressure.
What scores poorly
- Confusing var, let, const scoping.
- Using
anyliberally in TypeScript. - Not knowing what microtasks are.
- Memorized class syntax without understanding prototypal inheritance.
- Not handling edge cases in implemented utilities (empty inputs, nulls).
How to prepare
- Read “You Don’t Know JS” by Kyle Simpson for the deep JavaScript material.
- Read the TypeScript handbook end-to-end. Most candidates skim it and miss the advanced types.
- Implement common utilities from scratch: debounce, throttle, deepEqual, deepClone, curry, compose. Build the muscle.
- Practice reading code that uses prototypes, closures, and the event loop. Trace execution by hand.
- Build a small library or component in TypeScript with strict mode. The strict-mode constraint forces you to learn the type system properly.
Frequently Asked Questions
Are class-based components still tested?
Rarely in 2026. Functional components dominate. Class basics may come up but deep class-component knowledge is not the bar.
How important is the prototype chain in 2026?
Less centrally tested than 5 years ago, but still occasionally probed. Senior candidates should know it.
Is TypeScript universally adopted in 2026?
Effectively yes for new projects. Some legacy codebases remain in plain JS but the bar for new senior+ frontend hires assumes TypeScript fluency.
How deep does TypeScript get in interviews?
Generics with constraints, conditional types, mapped types are the typical depth. Senior interviewers may go deeper for type-system-heavy roles (library authors, framework engineers).
Do I need to memorize all utility types?
No. Memorize the most common (Partial, Pick, Omit, Record, ReturnType). Know the others exist; look them up as needed in real work.