JavaScript And TypeScript
Syntax Basics
Statements end with ; (ASI can insert them, but relying on it causes edge-case bugs — terminate explicitly). Blocks use {}. Comments: // and /* */.
if (x < y) {
console.log("smaller");
} else if (x === y) {
console.log("equal");
} else {
console.log("bigger");
}
for (let i = 0; i < 3; i++) console.log(i);
for (const item of [1, 2, 3]) console.log(item); // values
for (const key in { a: 1 }) console.log(key); // keys
Always use ===/!== (strict equality) — == does type coercion and surprises.
Variables And Scope
let count = 0; // block-scoped, reassignable
const name = "Ana"; // block-scoped, cannot rebind (but object contents are mutable)
var legacy = true; // function-scoped — avoid in new code
const prevents reassignment, not mutation: const arr = [1]; arr.push(2); is valid. let/const are hoisted but live in a “temporal dead zone” until their declaration executes — referencing them earlier throws.
Arrays And Objects
const nums = [1, 2, 3];
nums.map(n => n * 2);
nums.filter(n => n % 2 === 0);
nums.reduce((acc, n) => acc + n, 0);
nums.find(n => n > 1);
nums.includes(2);
nums.sort((a, b) => a - b); // default sort is string-based, pass a comparator
const user = { name: "Ana", age: 30 };
Object.keys(user);
Object.values(user);
Object.entries(user);
{ ...user, age: 31 }; // shallow copy with override
Destructuring And Spread
const { name, age = 18 } = user;
const [first, , third] = [1, 2, 3];
const { name: userName } = user; // rename while destructuring
function greet({ name, greeting = "Hi" }) {
return `${greeting}, ${name}`;
}
const merged = { ...defaults, ...overrides };
const combined = [...arr1, ...arr2];
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
Functions And Arrow Functions
function add(a, b) { return a + b; }
const addArrow = (a, b) => a + b; // implicit return
const noop = () => {};
const wrapped = (a) => { return a * 2; }; // explicit return needed with braces
Arrow functions do not bind their own this — they inherit it from the enclosing scope, which is why they’re preferred for callbacks inside class methods and event handlers.
Promises And Async Await
function fetchUser(id) {
return fetch(`/api/users/${id}`).then(r => r.json());
}
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error("load failed", err);
throw err;
}
}
const [a, b] = await Promise.all([fetchUser(1), fetchUser(2)]);
await Promise.allSettled([p1, p2]); // never rejects, reports each outcome
async functions always return a Promise; await only pauses inside an async function (or a top-level module).
Modules
// math.js
export function add(a, b) { return a + b; }
export default class Calculator {}
// main.js
import Calculator, { add } from "./math.js";
import * as math from "./math.js";
Node’s CommonJS (require/module.exports) still appears in older code and configs — ESM (import/export) is the standard for new code.
DOM Basics
const el = document.querySelector(".card");
const all = document.querySelectorAll(".card");
el.textContent = "Hello";
el.classList.add("active");
el.addEventListener("click", (ev) => {
ev.preventDefault();
console.log(ev.target);
});
const div = document.createElement("div");
div.append("child text");
parent.appendChild(div);
Prefer addEventListener over inline onclick attributes; use event delegation (listen on a parent, check ev.target) for dynamic lists.
TypeScript Types
let id: number = 1;
let name: string = "Ana";
let tags: string[] = [];
let user: { name: string; age?: number } = { name: "Ana" };
type Role = "admin" | "member";
interface User {
id: string;
email: string;
role: Role;
}
function greet(user: User): string {
return `Hi ${user.email}`;
}
type PartialUser = Partial<User>;
type UserEmail = Pick<User, "email">;
Use interface for object shapes you might extend; use type for unions, intersections, and utility-type compositions.
TypeScript Generics
function identity<T>(value: T): T {
return value;
}
interface Box<T> {
value: T;
}
function firstOf<T>(items: T[]): T | undefined {
return items[0];
}
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
NPM Basics
npm init -y
npm install express # adds to dependencies
npm install -D typescript # adds to devDependencies
npm install # installs from package-lock.json
npm run <script> # runs a script from package.json
npm ci # clean install, exact lockfile versions (CI-safe)
npx <package> # run a package without installing globally
Semver ranges in package.json: ^1.2.3 allows minor/patch updates, ~1.2.3 allows patch-only updates, exact 1.2.3 pins it.
Common Idioms
// optional chaining + nullish coalescing
const city = user?.address?.city ?? "unknown";
// short-circuit default
const port = process.env.PORT || 3000;
// debounce
function debounce(fn, ms) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
// immutable update
const updated = { ...state, count: state.count + 1 };