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 };