Skip to content

JavaScript and TypeScript Interview Guide for Java Full Stack

This guide consolidates JavaScript and TypeScript interview topics commonly asked in senior Java Full Stack interviews. It includes explanations, comparisons, examples, and diagrams.

JavaScript is a:

  • High-level language
  • Interpreted language
  • Prototype-based language
  • Dynamically typed language
  • Single-threaded language
  • Event-driven language

Initially it was created for browsers, but today it also runs on servers using Node.js.

console.log("Hello");

JavaScript has one Call Stack and executes one statement at a time.

flowchart TD
A[Task 1] --> B[Task 2]
B --> C[Task 3]

Asynchronous work is handled using Browser APIs/Node APIs, queues, and the Event Loop.

Execution Context is the environment in which JavaScript code executes.

Two phases:

  • Memory Creation Phase
  • Execution Phase
flowchart TD
A[Execution Context] --> B[Memory Phase]
A --> C[Execution Phase]
B --> D[Variables initialized]
B --> E[Functions stored]
C --> F[Execute line by line]
var a = 10;
function test() {
console.log(a);
}
test();

Variables and function declarations are hoisted.

console.log(a);
var a = 10;

Equivalent:

var a;
console.log(a);
a = 10;

Output:

undefined
Feature var let const
Scope Function Block Block
Redeclare Yes No No
Reassign Yes Yes No
Hoisted Yes Yes Yes
TDZ No Yes Yes
console.log(a);
let a = 10;

Produces:

ReferenceError

A closure allows an inner function to remember variables from an outer function.

function outer() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
const fn = outer();
fn();
fn();
fn();

Output:

1
2
3

Uses:

  • Data hiding
  • Private state
  • Memoization
  • React Hooks
let x = 10;
function outer() {
let x = 20;
function inner() {
console.log(x);
}
inner();
}
outer();

Output:

20
flowchart LR
A[Call Stack] --> B[Browser APIs]
B --> C[Microtask Queue]
B --> D[Macrotask Queue]
C --> E[Event Loop]
D --> E
E --> A
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");

Output:

Start
End
Promise
Timeout

Microtasks always execute before macrotasks.

flowchart TD
main --> test
test --> display
display --> Return

LIFO (Last In, First Out).

States:

stateDiagram-v2
[*] --> Pending
Pending --> Fulfilled
Pending --> Rejected
const promise = new Promise((resolve) => {
resolve("Success");
});
promise.then(console.log);
fetch(url)
.then(res => res.json())
.then(data => console.log(data))
.catch(console.error);
async function load() {
const response = await fetch(url);
const data = await response.json();
}

Internally uses Promises.

Promise Async/Await
then/catch await
Chain style Sequential style
Harder to read Cleaner
login(function () {
getProfile(function () {
getOrders(function () {
});
});
});

Solved using Promises and async/await.

const person = {
name: "John",
show() {
console.log(this.name);
}
};

Arrow functions inherit this lexically.

const add = (a, b) => a + b;

Characteristics:

  • Lexical this
  • No own arguments
  • Cannot be constructors
const person = {
walk() {}
};
const student = Object.create(person);
student.walk();

Prototype chain:

flowchart TD
Student --> Person
Person --> Object
Object --> Null
structuredClone(obj);

or

JSON.parse(JSON.stringify(obj));
5 == "5";
5 === "5";

Results:

true
false
const person = { name: "Raj", age: 30 };
const { name } = person;
const arr = [1, 2];
const newArr = [...arr, 3];
function add(...nums) {}
nums.map(x => x * 2);
nums.filter(x => x > 5);
nums.reduce((a, b) => a + b);
forEach map
No return Returns new array
No chaining Supports chaining
let timer;
function debounce(fn) {
clearTimeout(timer);
timer = setTimeout(fn, 500);
}

Executes a function at most once during a specified interval.

Useful for scroll, resize, and mousemove events.

function throttle(fn, delay) {
let waiting = false;
return (...args) => {
if (waiting) return;
fn(...args);
waiting = true;
setTimeout(() => waiting = false, delay);
};
}
parent.addEventListener("click", (e) => {
});

Improves performance by attaching one listener to a parent.

Local Storage Session Storage
Persists Cleared on tab close

Cookies are sent to the server. Local Storage remains in the browser.

export function add() {}
import { add } from "./math.js";

JavaScript uses a mark-and-sweep garbage collector.

let user = { name: "Alice" };
user = null;

Microtask Macrotask
Promise.then setTimeout
queueMicrotask setInterval
Higher priority Lower priority

Common causes:

  • Global variables
  • Uncleared timers
  • Event listeners
  • Closures retaining large objects
function add(a) {
return function (b) {
return a + b;
};
}
console.log(add(5)(10));
const double = x => x * 2;
const square = x => x * x;
console.log(square(double(5)));
  • Debouncing
  • Throttling
  • Memoization
  • Lazy loading
  • Tree shaking
  • Code splitting
  • Virtual scrolling
  • Efficient DOM updates

Prototype:

function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function () {
console.log("Hi");
};

Class:

class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log("Hi");
}
}

Classes are syntactic sugar over prototypes.

function* numbers() {
yield 1;
yield 2;
yield 3;
}
const gen = numbers();
console.log(gen.next());
console.log(gen.next());
const person = { name: "John" };
function greet(city) {
console.log(this.name + " " + city);
}
greet.call(person, "London");
greet.apply(person, ["London"]);
const fn = greet.bind(person);
fn("London");
flowchart TD
Body --> Div --> Button

Use:

event.stopPropagation();
user?.address?.city;
const name = userName ?? "Guest";
ES Modules CommonJS
import/export require/module.exports
Static Dynamic
Tree shaking No tree shaking

Benefits:

  • Static typing
  • Interfaces
  • Generics
  • Compile-time checking
  • Better IntelliSense
  • Easier refactoring
JavaScript TypeScript
Dynamic Static
Runtime errors Compile-time checking
No interfaces Interfaces
let id: number = 1;
let name: string = "Raj";
let active: boolean = true;
interface Employee {
id: number;
name: string;
}
type Employee = {
id: number;
};
let id: number | string;
type A = { name: string };
type B = { age: number };
type Person = A & B;
function identity<T>(value: T): T {
return value;
}
interface ApiResponse<T> {
data: T;
}
type User = {
name: string;
age: number;
};
type Keys = keyof User;
  • Partial
  • Required
  • Readonly
  • Pick
  • Omit
  • Record
  • Exclude
  • Extract
  • NonNullable
interface User {
id: number;
name: string;
email?: string;
}
type UpdateUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type UserSummary = Pick<User, "id" | "name">;
type UserWithoutEmail = Omit<User, "email">;
enum Status {
SUCCESS,
FAILED
}
@Component({
selector: "app-root"
})
class AppComponent {}
any unknown
Unsafe Type-safe
let value: unknown = "Hello";
if (typeof value === "string") {
console.log(value.toUpperCase());
}
function fail(message: string): never {
throw new Error(message);
}
interface UserProps {
name: string;
age: number;
}
function User({ name, age }: UserProps) {
return <h1>{name} - {age}</h1>;
}
type Circle = {
kind: "circle";
radius: number;
};
type Square = {
kind: "square";
side: number;
};
type Shape = Circle | Square;
function area(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius * shape.radius;
}
return shape.side * shape.side;
}
type ReadOnly<T> = {
readonly [P in keyof T]: T[P];
};
type IsString<T> = T extends string ? true : false;
type Return<T> = T extends (...args: any[]) => infer R ? R : never;
function add() {
return 10;
}
type Result = Return<typeof add>;
interface ApiResponse<T> {
data: T;
status: number;
}
async function get<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
const data = await response.json();
return {
data,
status: response.status
};
}

Usage:

interface User {
id: number;
name: string;
}
const response = await get<User>("/users/1");
console.log(response.data.name);
  • Master JavaScript fundamentals before advanced concepts.
  • Understand the Event Loop, microtasks, closures, and async programming thoroughly.
  • Learn prototypes as well as ES6 classes.
  • Become comfortable with functional programming concepts such as currying and composition.
  • Use TypeScript generics, utility types, discriminated unions, mapped types, and conditional types to write reusable, type-safe applications.
  • These topics collectively represent the most common JavaScript and TypeScript interview areas for senior Java Full Stack developers.