Skip to content

Instantly share code, notes, and snippets.

@kjanat
Last active July 17, 2025 01:35
Show Gist options
  • Select an option

  • Save kjanat/07476aa08c4e703429b29c46b21892d3 to your computer and use it in GitHub Desktop.

Select an option

Save kjanat/07476aa08c4e703429b29c46b21892d3 to your computer and use it in GitHub Desktop.
JavaScript & TypeScript Beginner's Manual

JavaScript & TypeScript Beginner's Manual

A comprehensive guide for developers new to JavaScript and its ecosystem - Updated for 2025

Table of Contents

  1. Getting Started
  2. JavaScript Fundamentals
  3. Modern JavaScript (ES6+)
  4. Asynchronous JavaScript & Event Loop
  5. Introduction to TypeScript
  6. JavaScript Ecosystem
  7. Development Tools
  8. Best Practices
  9. Quick Reference
  10. Mini Projects
  11. Next Steps
Detailed Table of Contents

Getting Started

What is JavaScript?

JavaScript is a versatile, high-level programming language primarily used for:

  • Web Development: Making websites interactive
  • Server-Side Development: Building APIs and backends (Node.js)
  • Mobile Apps: React Native, Ionic
  • Desktop Apps: Electron
  • Game Development: Browser-based games

Setting Up Your Environment

Option 1: Browser Console (Immediate Start)

  • Open any browser → F12 → Console tab
  • Start typing JavaScript immediately

Option 2: Local Development

  1. Install Node.js (LTS v22+ recommended)
  2. Install a code editor (VS Code recommended)
  3. Create a .js file and run with node filename.js

Option 3: Online Playgrounds

Your First JavaScript Program

// Hello World
console.log("Hello, World!");

// Variables and basic math
let name = "JavaScript";
let year = 2025;
console.log(`Welcome to ${name} in ${year}!`);

JavaScript Fundamentals

Variables and Data Types

// Variable declarations
var oldWay = "avoid this";        // Function-scoped, hoisted
let modernWay = "use this";       // Block-scoped
const constant = "binding is fixed"; // Block-scoped, binding immutable

// Primitive data types
let message = "Hello World";
let count = 42;
let isActive = true;
let emptyValue = null;
let notDefined = undefined;
let uniqueId = Symbol('unique');
let bigNumber = 123n; // Supported in all modern environments

// Type checking
console.log(typeof message);    // "string"
console.log(typeof count);      // "number"
console.log(typeof isActive);   // "boolean"

// Important: const objects/arrays are still mutable
const user = { name: "Alice" };
user.name = "Bob"; // This works! Only the binding is immutable
console.log(user); // { name: "Bob" }

Objects and Arrays

// Objects
const person = {
  name: "Alice",
  age: 30,
  city: "New York",
  greet: function() {
    return `Hello, I'm ${this.name}`;
  }
};

// Accessing object properties
console.log(person.name);        // Dot notation
console.log(person["age"]);      // Bracket notation
console.log(person.greet());     // Method call

// Modern object property checking
console.log(Object.hasOwn(person, 'name')); // true (preferred over hasOwnProperty)

// Arrays
const fruits = ["apple", "banana", "orange"];
const scores = [1, 2, 3, 4, 5];
const mixedData = [1, "hello", true, null];

// Array methods
fruits.push("grape");            // Add to end
fruits.pop();                    // Remove from end
fruits.unshift("mango");         // Add to beginning
fruits.shift();                  // Remove from beginning
console.log(fruits.length);      // Array length

// Modern array access
console.log(scores.at(-1));      // 5 (last element, ES2022)
console.log(scores.at(-2));      // 4 (second to last)

Functions

// Function declaration
function greet(name) {
  return `Hello, ${name}!`;
}

// Function expression
const greetExpression = function(name) {
  return `Hello, ${name}!`;
};

// Arrow function (ES6+)
const greetArrow = (name) => {
  return `Hello, ${name}!`;
};

// Short arrow function
const greetShort = name => `Hello, ${name}!`;

// Function with default parameters
function introduce(name, age = 25) {
  return `I'm ${name}, ${age} years old`;
}

// Rest parameters
function calculateSum(...values) {
  return values.reduce((total, num) => total + num, 0);
}

console.log(calculateSum(1, 2, 3, 4)); // 10

Control Flow

// If/else statements
const userAge = 18;

if (userAge >= 18) {
  console.log("Adult");
} else if (userAge >= 13) {
  console.log("Teenager");
} else {
  console.log("Child");
}

// Ternary operator
const status = userAge >= 18 ? "Adult" : "Minor";

// Switch statement
const dayOfWeek = "Monday";
switch (dayOfWeek) {
  case "Monday":
    console.log("Start of work week");
    break;
  case "Friday":
    console.log("TGIF!");
    break;
  default:
    console.log("Regular day");
}

// Loops
// For loop
for (let i = 0; i < 5; i++) {
  console.log(i);
}

// For...of loop (arrays)
const colors = ["red", "green", "blue"];
for (const color of colors) {
  console.log(color);
}

// For...in loop (objects)
const vehicle = { make: "Toyota", model: "Camry", year: 2025 };
for (const key in vehicle) {
  console.log(`${key}: ${vehicle[key]}`);
}

// While loop
let counter = 0;
while (counter < 3) {
  console.log(counter);
  counter++;
}

Modern JavaScript (ES6+)

Template Literals

const firstName = "John";
const userAge = 25;

// Old way
const oldMessage = "Hello, my name is " + firstName + " and I'm " + userAge + " years old";

// Modern way
const newMessage = `Hello, my name is ${firstName} and I'm ${userAge} years old`;

// Multi-line strings
const htmlTemplate = `
  <div>
    <h1>${firstName}</h1>
    <p>Age: ${userAge}</p>
  </div>
`;

Destructuring

// Array destructuring
const coordinates = [1, 2, 3, 4, 5];
const [x, y, ...remaining] = coordinates;
console.log(x);         // 1
console.log(y);         // 2
console.log(remaining); // [3, 4, 5]

// Object destructuring
const userProfile = { name: "Alice", email: "[email protected]", age: 30 };
const { name, email } = userProfile;
console.log(name);   // "Alice"
console.log(email);  // "[email protected]"

// Destructuring with renaming
const { name: displayName, age: displayAge } = userProfile;

// Destructuring with defaults
const { city = "Unknown" } = userProfile;

Spread Operator

// Array spread
const firstBatch = [1, 2, 3];
const secondBatch = [4, 5, 6];
const allNumbers = [...firstBatch, ...secondBatch]; // [1, 2, 3, 4, 5, 6]

// Object spread
const basicInfo = { name: "John", age: 30 };
const employeeInfo = { ...basicInfo, job: "Developer", salary: 50000 };

// Function arguments
function sum(a, b, c) {
  return a + b + c;
}
const numberList = [1, 2, 3];
console.log(sum(...numberList)); // 6

Optional Chaining & Nullish Coalescing

// Optional chaining (?.) - ES2020
const userData = {
  profile: {
    social: {
      twitter: "@johndoe"
    }
  }
};

// Safe property access
console.log(userData?.profile?.social?.twitter); // "@johndoe"
console.log(userData?.profile?.social?.linkedin); // undefined (no error)

// Optional method calls
const api = {
  getData: () => "some data"
};
console.log(api.getData?.()); // "some data"
console.log(api.postData?.()); // undefined (no error)

// Optional array access
const items = ["apple", "banana"];
console.log(items?.[0]); // "apple"
console.log(items?.[10]); // undefined

// Nullish coalescing (??) - ES2020
const config = {
  timeout: 0,
  retries: null,
  debug: false
};

// Only null/undefined trigger fallback, not falsy values
console.log(config.timeout ?? 5000);  // 0 (not 5000!)
console.log(config.retries ?? 3);     // 3
console.log(config.debug ?? true);    // false (not true!)

// Compare with || operator
console.log(config.timeout || 5000);  // 5000 (falsy value replaced)
console.log(config.debug || true);    // true (falsy value replaced)

Set Methods (ES2025 - Now Stable!)

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

// Union - combine sets
const union = setA.union(setB);
console.log(union); // Set(6) {1, 2, 3, 4, 5, 6}

// Intersection - common elements
const intersection = setA.intersection(setB);
console.log(intersection); // Set(2) {3, 4}

// Difference - elements in setA but not setB
const difference = setA.difference(setB);
console.log(difference); // Set(2) {1, 2}

// Symmetric difference - elements in either set, but not both
const symmetricDifference = setA.symmetricDifference(setB);
console.log(symmetricDifference); // Set(4) {1, 2, 5, 6}

// Set relationship checks
console.log(setA.isSubsetOf(setB)); // false
console.log(setA.isSupersetOf(setB)); // false
console.log(setA.isDisjointFrom(new Set([7, 8]))); // true

Promise.try() (ES2025)

// Promise.try() - unified wrapper for sync/async operations
async function processData(data) {
  return Promise.try(() => {
    if (data.sync) {
      return data.value * 2; // Synchronous operation
    } else {
      return fetch('/api/process').then(r => r.json()); // Async operation
    }
  })
  .then(result => {
    console.log('Result:', result);
    return result;
  })
  .catch(error => {
    console.error('Error:', error);
    throw error;
  });
}

// Both sync and async operations are handled consistently
await processData({ sync: true, value: 5 });
await processData({ sync: false });

Import Attributes (ES2025)

// Old import assertions (deprecated)
// import data from './data.json' assert { type: 'json' };

// New import attributes syntax
import data from './data.json' with { type: 'json' };
import styles from './styles.css' with { type: 'css' };

// Dynamic imports with attributes
const config = await import('./config.json', {
  with: { type: 'json' }
});

RegExp.escape() (ES2025)

// Safely escape special regex characters
const userInput = "Hello (world) [2025]";
const escapedInput = RegExp.escape(userInput);
console.log(escapedInput); // "Hello \\(world\\) \\[2025\\]"

// Safe to use in regex patterns
const regex = new RegExp(escapedInput);
console.log(regex.test("Hello (world) [2025]")); // true

// Before ES2025, you had to manually escape:
function oldEscape(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

Array Methods

const numberSet = [1, 2, 3, 4, 5];

// map - transform each element
const doubled = numberSet.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter - keep elements that pass test
const evenNumbers = numberSet.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]

// reduce - accumulate values
const total = numberSet.reduce((acc, num) => acc + num, 0);
console.log(total); // 15

// find - get first element that matches
const found = numberSet.find(num => num > 3);
console.log(found); // 4

// includes - check if element exists
console.log(numberSet.includes(3)); // true

// forEach - execute function for each element
numberSet.forEach(num => console.log(num));

// Modern additions
const words = ["apple", "banana", "cherry"];
const longWords = words.filter(word => word.length > 5);
console.log(longWords); // ["banana", "cherry"]

Float16Array & Math.f16round() (ES2025)

// 16-bit floating point arrays for memory efficiency
const float16Array = new Float16Array([1.5, 2.7, 3.14159]);
console.log(float16Array); // Float16Array(3) [1.5, 2.7, 3.14159]

// Round to 16-bit precision
const rounded = Math.f16round(3.14159265359);
console.log(rounded); // 3.140625 (16-bit precision)

// Useful for graphics, ML, and memory-constrained applications
const vertices = new Float16Array([
   0.0,  1.0   // Top
]);

Popular Libraries & Frameworks

This section provides a brief overview of some of the most popular and influential libraries and frameworks in the JavaScript ecosystem.

React (UI Library)

// Functional component with hooks
import React, { useState, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Express.js (Backend Framework)

// Basic Express server
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

Vite (Modern build tool)

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000
  }
});

Classes

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  getAge() {
    return this.age;
  }
  
  static getSpecies() {
    return "Homo sapiens";
  }
}

class Student extends Person {
  constructor(name, age, school) {
    super(name, age); // Call parent constructor
    this.school = school;
  }
  
  study() {
    return `${this.name} is studying at ${this.school}`;
  }
}

const student = new Student("Alice", 20, "MIT");
console.log(student.greet());  // "Hello, I'm Alice"
console.log(student.study());  // "Alice is studying at MIT"

Modules (Import/Export)

// math.js - Named exports
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

export const PI = 3.14159;

// utils.js - Default export
export default function formatCurrency(amount) {
  return `${amount.toFixed(2)}`;
}

// main.js - Importing
import formatCurrency from './utils.js';           // Default import
import { add, multiply, PI } from './math.js';     // Named imports
import { add as addNumbers } from './math.js';     // Rename import
import * as mathUtils from './math.js';            // Namespace import

// Dynamic imports (ES2020)
async function loadModule() {
  const { add } = await import('./math.js');
  console.log(add(2, 3));
}

// Top-level await (ES2022) - in modules only
const mathModule = await import('./math.js');
console.log(mathModule.add(5, 10));

Module Resolution Gotchas

Warning

Common Pitfall: Mixing CommonJS (CJS) and ES Modules (ESM) can cause ERR_REQUIRE_ESM errors.

Tip

2025 Recommendation: Use ESM (ES Modules) as your default choice. CommonJS should only be used for legacy codebases or specific compatibility requirements.

The Problem:

// ❌ This will fail if the imported module is ESM-only
const express = require('express'); // CJS syntax
// Error: ERR_REQUIRE_ESM: require() of ES Module not supported

// ✅ Use this instead
import express from 'express'; // ESM syntax

Why ESM is the Future (2025):

  • Better tree-shaking support (smaller bundles)
  • Native browser compatibility
  • Future-proof for tooling ecosystem
  • Async module loading capabilities
  • Static analysis benefits

Solutions:

  1. Set "type": "module" in package.json (recommended)
  2. Use .mjs extension for ES modules
  3. Use .cjs extension for CommonJS modules

package.json for pure ESM project (recommended for 2025):

{
  "type": "module",
  "scripts": {
    "start": "node src/index.js"
  }
}

Node.js 22 Mixed Environment Support:

// With Node.js 22's --experimental-require-module flag
// You can now require ESM modules synchronously in CommonJS
const { readFile } = require('fs/promises'); // Now works!

// For importing CJS in ESM (still needed occasionally)
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const someCommonJSModule = require('old-cjs-package');

// For importing ESM in CJS (async only)
const { default: someESModule } = await import('es-module-package');

Asynchronous JavaScript & Event Loop

Understanding the Event Loop

Event Loop Flow:

graph TD
    A[Call Stack] --> B{Stack Empty?}
    B -->|No| A
    B -->|Yes| C[Check Microtask Queue]
    C --> D{Microtasks Available?}
    D -->|Yes| E[Execute Microtask]
    E --> A
    D -->|No| F[Check Macrotask Queue]
    F --> G{Macrotasks Available?}
    G -->|Yes| H[Execute Macrotask]
    H --> A
    G -->|No| I[Wait for I/O]
    I --> C
Loading

Task Queue Priority:

flowchart LR
    A[Synchronous Code<br/>Call Stack] --> B[Microtasks<br/>Higher Priority]
    B --> C[Macrotasks<br/>Lower Priority]
    
    B1[Promise.then<br/>queueMicrotask<br/>MutationObserver] --> B
    C1[setTimeout<br/>setInterval<br/>setImmediate<br/>DOM Events] --> C
Loading

Key Concepts:

  • Call Stack: Where your synchronous code runs
  • Microtask Queue: Promises, queueMicrotask (higher priority)
  • Macrotask Queue: setTimeout, setInterval, DOM events (lower priority)
// Event loop demonstration
console.log('1');                               // Synchronous
setTimeout(() => console.log('2'), 0);          // Macrotask
Promise.resolve().then(() => console.log('3')); // Microtask
console.log('4');                               // Synchronous

// Output: 1, 4, 3, 2
Test Case Output
Expected 1, 2, 3, 4
Actual 1, 4, 3, 2

Why? Microtasks run before macrotasks!

Modern Async Patterns (Use These!)

Important

Callbacks are legacy patterns. Don't start new projects with callbacks - use Promises and async/await instead. Callbacks lead to "callback hell" and make error handling difficult.

The Evolution:

  1. Callbacks (legacy) → Hard to read, error-prone
  2. Promises (ES6) → Chainable, better error handling
  3. Async/Await (ES2017) → Synchronous-looking async code

Callbacks (Legacy - Learn but Don't Use)

// ❌ DON'T write new code like this
function fetchUserData(userId, callback) {
  setTimeout(() => {
    const userData = { id: userId, name: "John" };
    callback(userData);
  }, 1000);
}

fetchUserData(1, (data) => {
  console.log("Received:", data);
});

// ❌ This is "callback hell" - avoid it!
fetchUserData(1, (user) => {
  fetchUserPosts(user.id, (posts) => {
    fetchPostComments(posts[0].id, (comments) => {
      console.log("Deep nesting is impossible to maintain!");
    });
  });
});

Promises

// Creating a Promise
function fetchUser(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) {
        resolve({ id: userId, name: "John Doe" });
      } else {
        reject(new Error("Invalid user ID"));
      }
    }, 1000);
  });
}

// Using Promises
fetchUser(1)
  .then(user => {
    console.log("User:", user);
    return fetchUser(2); // Chain another promise
  })
  .then(user2 => {
    console.log("User 2:", user2);
  })
  .catch(error => {
    console.error("Error:", error.message);
  })
  .finally(() => {
    console.log("Operation completed");
  });

// Promise utilities (ES2021+)
const promises = [fetchUser(1), fetchUser(2), fetchUser(3)];

// All must succeed
Promise.all(promises)
  .then(users => console.log("All users:", users));

// First to resolve wins
Promise.race(promises)
  .then(firstUser => console.log("First user:", firstUser));

// All settled (success or failure)
Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`User ${index + 1}:`, result.value);
      } else {
        console.log(`User ${index + 1} failed:`, result.reason);
      }
    });
  });

// First to resolve (ignores rejections until all fail)
Promise.any(promises)
  .then(user => console.log("First successful user:", user))
  .catch(error => console.log("All failed:", error));

Async/Await

// Async function
async function getUserData(userId) {
  try {
    const user = await fetchUser(userId);
    console.log("User:", user);
    
    // You can use await multiple times
    const user2 = await fetchUser(2);
    console.log("User 2:", user2);
    
    return user;
  } catch (error) {
    console.error("Error:", error.message);
    throw error; // Re-throw if needed
  }
}

// Call async function
getUserData(1);

// Parallel execution with Promise.all
async function getMultipleUsers() {
  try {
    const [user1, user2, user3] = await Promise.all([
      fetchUser(1),
      fetchUser(2),
      fetchUser(3)
    ]);
    console.log("All users:", user1, user2, user3);
  } catch (error) {
    console.error("One or more requests failed:", error);
  }
}

// Top-level await (ES2022) - only in modules
const mainUser = await fetchUser(1);
console.log("Main user:", mainUser);

Fetch API

// GET request
async function getUsers() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const users = await response.json();
    console.log(users);
    return users;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

// POST request with error handling
async function createUser(userData) {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(userData)
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`Server error: ${errorData.message}`);
    }
    
    const newUser = await response.json();
    console.log('Created user:', newUser);
    return newUser;
  } catch (error) {
    console.error('Error creating user:', error);
    throw error;
  }
}

// Fetch with timeout
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, {
      signal: controller.signal
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timed out');
    }
    throw error;
  }
}

Introduction to TypeScript

What is TypeScript?

TypeScript is a superset of JavaScript that adds static type checking. It compiles to plain JavaScript and helps catch errors during development.

Benefits:

  • Catch errors at compile time
  • Better IDE support (autocomplete, refactoring)
  • Self-documenting code
  • Easier refactoring in large codebases

TypeScript Configuration (tsconfig.json)

{
  "compilerOptions": {
    "target": "ES2025",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2025", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true,
    "allowImportingTsExtensions": true,
    "noEmit": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Key strict mode options:

  • noImplicitAny: Error on variables with implicit any type
  • strictNullChecks: null and undefined must be explicitly handled
  • strictFunctionTypes: Stricter checking of function types
  • noImplicitReturns: Error when not all code paths return a value

Basic Types

// Basic type annotations
let userName: string = "John";
let userAge: number = 30;
let isActive: boolean = true;
let hobbies: string[] = ["reading", "coding"];
let scores: Array<number> = [1, 2, 3];

// Object type
let person: { name: string; age: number } = {
  name: "Alice",
  age: 25
};

// Function types
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// Arrow function with types
const add = (a: number, b: number): number => a + b;

// Optional parameters
function introduce(name: string, age?: number): string {
  return age ? `I'm ${name}, ${age} years old` : `I'm ${name}`;
}

// Union types
let id: string | number = "user123";
id = 42; // Also valid

// Literal types
let status: "pending" | "approved" | "rejected" = "pending";

// Arrays with union types
let mixedArray: (string | number)[] = ["hello", 42, "world"];

Interfaces and Type Aliases

// Interface definition
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // Optional property
  readonly createdAt: Date; // Read-only property
}

// Using interface
const user: User = {
  id: 1,
  name: "John Doe",
  email: "[email protected]",
  createdAt: new Date()
};

// Interface for functions
interface Calculator {
  (a: number, b: number): number;
}

const multiply: Calculator = (a, b) => a * b;

// Extending interfaces
interface Employee extends User {
  department: string;
  salary: number;
}

// Type aliases
type Status = "pending" | "approved" | "rejected";
type ID = string | number;

// Advanced type operations
type UserKeys = keyof User;   // "id" | "name" | "email" | "age" | "createdAt"
type UserName = User['name']; // string
type RequiredUser = Required<User>; // Makes all properties required
type PartialUser = Partial<User>;   // Makes all properties optional
type UserWithoutId = Omit<User, 'id'>; // Removes 'id' property
type UserIdAndName = Pick<User, 'id' | 'name'>; // Only 'id' and 'name'

// Satisfies operator (TypeScript 4.9+)
const userConfig = {
  name: "John",
  age: 30,
  isActive: true
} satisfies Record<string, string | number | boolean>;

// userConfig.name is inferred as "John", not string
// but still must satisfy the Record constraint

Generics

// Generic function
function identity<T>(arg: T): T {
  return arg;
}

// Usage
const stringResult = identity<string>("hello"); // string
const numberResult = identity(42); // number (inferred)

// Generic interface
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// Generic constraints
interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello");   // Works, string has length
logLength([1, 2, 3]); // Works, array has length
// logLength(42); // Error, number doesn't have length

// Conditional types
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false

// Mapped types
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type ReadonlyUser = Readonly<User>;

Classes in TypeScript

abstract class Animal {
  protected name: string; // Protected: accessible in this class and subclasses
  private age: number;    // Private: only accessible in this class
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  
  public speak(): string { // Public: accessible everywhere
    return `${this.name} makes a sound`;
  }
  
  abstract makeSound(): string; // Must be implemented by subclasses
  
  getAge(): number {
    return this.age;
  }
}

class Dog extends Animal {
  private breed: string;
  
  constructor(name: string, age: number, breed: string) {
    super(name, age);
    this.breed = breed;
  }
  
  makeSound(): string {
    return "Woof!";
  }
  
  speak(): string {
    return `${this.name} barks: ${this.makeSound()}`;
  }
  
  getBreed(): string {
    return this.breed;
  }
}

// Interface implementation
interface Flyable {
  fly(): void;
  altitude: number;
}

class Bird implements Flyable {
  altitude: number = 0;
  
  fly(): void {
    this.altitude += 100;
    console.log(`Flying at ${this.altitude} feet`);
  }
}

JavaScript Ecosystem

Node.js and npm

# Check versions (Node 22+ recommended)
node --version
npm --version

# Initialize a new project
npm init -y

# Install packages
npm install express          # Production dependency
npm install -D typescript @types/node jest  # Development dependencies
npm install -g pnpm          # Global installation (alternative package manager)

# Run scripts (defined in package.json)
npm start
npm test
npm run build
npm run dev

# Update packages
npm update
npm outdated  # Check for outdated packages

Modern package.json (2025):

{
  "name": "my-project",
  "version": "1.0.0",
  "type": "module",
  "engines": {
    "node": ">=22.0.0"
  },
  "scripts": {
    "start": "node dist/index.js",
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "test": "jest",
    "lint": "eslint src/**/*.ts",
    "format": "prettier --write src/**/*.ts"
  },
  "dependencies": {
    "express": "^4.19.0",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^20.11.0",
    "eslint": "^9.0.0",
    "jest": "^31.0.0",
    "prettier": "^3.2.0",
    "tsx": "^4.7.0",
    "typescript": "^5.4.0"
  }
}

Popular Frameworks Overview

React (UI Library)

import React, { useState, useEffect } from 'react';

interface CounterProps {
  initialValue?: number;
}

function Counter({ initialValue = 0 }: CounterProps) {
  const [count, setCount] = useState(initialValue);
  
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(count - 1)}>
        Decrement
      </button>
    </div>
  );
}

export default Counter;

Express.js (Backend Framework)

import express from 'express';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';

const app = express();
const prisma = new PrismaClient();

app.use(express.json());

// Validation schemas
const CreateUserSchema = z.object({
  name: z.string().min(1, 'Name is required'),
  email: z.string().email('Invalid email format')
});

const UpdateUserSchema = CreateUserSchema.partial();

// Routes
app.get('/users', async (req, res) => {
  try {
    const users = await prisma.user.findMany({
      orderBy: { createdAt: 'desc' }
    });
    res.json(users);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch users' });
  }
});

app.get('/users/:id', async (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const user = await prisma.user.findUnique({
      where: { id }
    });
    
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
    
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch user' });
  }
});

app.post('/users', async (req, res) => {
  try {
    const userData = CreateUserSchema.parse(req.body);
    
    const newUser = await prisma.user.create({
      data: userData
    });
    
    res.status(201).json(newUser);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ 
        error: 'Validation failed', 
        details: error.errors 
      });
    }
    
    // Prisma unique constraint error
    if ((error as any).code === 'P2002') {
      return res.status(400).json({ error: 'Email already exists' });
    }
    
    res.status(500).json({ error: 'Failed to create user' });
  }
});

app.put('/users/:id', async (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const updateData = UpdateUserSchema.parse(req.body);
    
    const updatedUser = await prisma.user.update({
      where: { id },
      data: updateData
    });
    
    res.json(updatedUser);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ 
        error: 'Validation failed', 
        details: error.errors 
      });
    }
    
    if ((error as any).code === 'P2025') {
      return res.status(404).json({ error: 'User not found' });
    }
    
    res.status(500).json({ error: 'Failed to update user' });
  }
});

app.delete('/users/:id', async (req, res) => {
  try {
    const id = parseInt(req.params.id);
    
    await prisma.user.delete({
      where: { id }
    });
    
    res.status(204).send();
  } catch (error) {
    if ((error as any).code === 'P2025') {
      return res.status(404).json({ error: 'User not found' });
    }
    
    res.status(500).json({ error: 'Failed to delete user' });
  }
});

// Graceful shutdown
process.on('SIGINT', async () => {
  await prisma.$disconnect();
  process.exit();
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

export default app;

Run the server:

npx tsx src/app.ts

2. React Todo App with TypeScript + ESLint/Prettier

package.json:

{
  "name": "react-todo-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "lint:fix": "eslint . --ext ts,tsx --fix",
    "format": "prettier --write \"src/**/*.{ts,tsx,css}\""
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.55",
    "@types/react-dom": "^18.2.19",
    "@typescript-eslint/eslint-plugin": "^7.0.0",
    "@typescript-eslint/parser": "^7.0.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^9.0.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "prettier": "^3.2.5",
    "typescript": "^5.4.0",
    "vite": "^5.1.0"
  }
}

eslint.config.js:

import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from '@typescript-eslint/eslint-plugin'
import tsParser from '@typescript-eslint/parser'

export default [
  { ignores: ['dist'] },
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parser: tsParser,
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
      '@typescript-eslint': tseslint,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
      '@typescript-eslint/no-unused-vars': 'error',
      'prefer-const': 'error',
      'no-console': 'warn',
    },
  },
]

prettier.config.js:

export default {
  semi: true,
  singleQuote: true,
  tabWidth: 2,
  trailingComma: 'es5',
  printWidth: 80,
  bracketSpacing: true,
  arrowParens: 'avoid',
};

src/types.ts:

export interface Todo {
  id: number;
  text: string;
  completed: boolean;
  createdAt: Date;
}

export type TodoFilter = 'all' | 'active' | 'completed';

src/TodoApp.tsx:

import React, { useState, useCallback, useMemo } from 'react';
import { Todo, TodoFilter } from './types';

const TodoApp: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [filter, setFilter] = useState<TodoFilter>('all');
  const [newTodoText, setNewTodoText] = useState('');

  const addTodo = useCallback((text: string) => {
    if (text.trim()) {
      const newTodo: Todo = {
        id: Date.now(),
        text: text.trim(),
        completed: false,
        createdAt: new Date()
      };
      setTodos(prev => [...prev, newTodo]);
      setNewTodoText('');
    }
  }, []);

  const toggleTodo = useCallback((id: number) => {
    setTodos(prev => 
      prev.map(todo => 
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }, []);

  const deleteTodo = useCallback((id: number) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  }, []);

  const filteredTodos = useMemo(() => {
    switch (filter) {
      case 'active':
        return todos.filter(todo => !todo.completed);
      case 'completed':
        return todos.filter(todo => todo.completed);
      default:
        return todos;
    }
  }, [todos, filter]);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    addTodo(newTodoText);
  };

  return (
    <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
      <h1>Todo App</h1>
      
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={newTodoText}
          onChange={(e) => setNewTodoText(e.target.value)}
          placeholder="Add a new todo..."
          style={{ width: '70%', padding: '10px', marginRight: '10px' }}
        />
        <button type="submit" disabled={!newTodoText.trim()}>
          Add Todo
        </button>
      </form>

      <div style={{ margin: '20px 0' }}>
        {(['all', 'active', 'completed'] as TodoFilter[]).map(filterOption => (
          <button
            key={filterOption}
            onClick={() => setFilter(filterOption)}
            style={{
              marginRight: '10px',
              padding: '5px 10px',
              backgroundColor: filter === filterOption ? '#007bff' : '#f8f9fa',
              color: filter === filterOption ? 'white' : 'black',
              border: '1px solid #ccc',
              borderRadius: '4px'
            }}
          >
            {filterOption.charAt(0).toUpperCase() + filterOption.slice(1)}
          </button>
        ))}
      </div>

      <ul style={{ listStyle: 'none', padding: 0 }}>
        {filteredTodos.map(todo => (
          <li
            key={todo.id}
            style={{
              display: 'flex',
              alignItems: 'center',
              padding: '10px',
              borderBottom: '1px solid #eee'
            }}
          >
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
              style={{ marginRight: '10px' }}
            />
            <span
              style={{
                flex: 1,
                textDecoration: todo.completed ? 'line-through' : 'none',
                color: todo.completed ? '#999' : 'black'
              }}
            >
              {todo.text}
            </span>
            <button
              onClick={() => deleteTodo(todo.id)}
              style={{
                backgroundColor: '#dc3545',
                color: 'white',
                border: 'none',
                padding: '5px 10px',
                borderRadius: '4px'
              }}
            >
              Delete
            </button>
          </li>
        ))}
      </ul>

      {filteredTodos.length === 0 && (
        <p style={{ textAlign: 'center', color: '#999', marginTop: '20px' }}>
          No todos to show
        </p>
      )}
    </div>
  );
};

export default TodoApp;

3. Complete Testing Example

src/calculator.ts:

export class Calculator {
  private history: string[] = [];

  add(a: number, b: number): number {
    const result = a + b;
    this.history.push(`${a} + ${b} = ${result}`);
    return result;
  }

  subtract(a: number, b: number): number {
    const result = a - b;
    this.history.push(`${a} - ${b} = ${result}`);
    return result;
  }

  multiply(a: number, b: number): number {
    const result = a * b;
    this.history.push(`${a} * ${b} = ${result}`);
    return result;
  }

  divide(a: number, b: number): number {
    if (b === 0) {
      throw new Error('Division by zero is not allowed');
    }
    const result = a / b;
    this.history.push(`${a} / ${b} = ${result}`);
    return result;
  }

  getHistory(): string[] {
    return [...this.history];
  }

  clearHistory(): void {
    this.history = [];
  }
}

src/api.ts:

export interface User {
  id: number;
  name: string;
  email: string;
}

export class UserApi {
  constructor(private baseUrl: string) {}

  async getUser(id: number): Promise<User> {
    const response = await fetch(`${this.baseUrl}/users/${id}`);
    
    if (!response.ok) {
      if (response.status === 404) {
        throw new Error(`User with id ${id} not found`);
      }
      throw new Error(`API error: ${response.status}`);
    }
    
    return response.json();
  }

  async createUser(userData: Omit<User, 'id'>): Promise<User> {
    const response = await fetch(`${this.baseUrl}/users`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(userData),
    });

    if (!response.ok) {
      throw new Error(`Failed to create user: ${response.status}`);
    }

    return response.json();
  }
}

src/tests/calculator.test.ts:

import { describe, test, expect, beforeEach } from 'vitest';
import { Calculator } from '../calculator';

describe('Calculator', () => {
  let calculator: Calculator;

  beforeEach(() => {
    calculator = new Calculator();
  });

  describe('basic operations', () => {
    test('should add two numbers correctly', () => {
      expect(calculator.add(2, 3)).toBe(5);
      expect(calculator.add(-1, 1)).toBe(0);
      expect(calculator.add(0.1, 0.2)).toBeCloseTo(0.3);
    });

    test('should subtract two numbers correctly', () => {
      expect(calculator.subtract(5, 3)).toBe(2);
      expect(calculator.subtract(1, 1)).toBe(0);
      expect(calculator.subtract(-1, -1)).toBe(0);
    });

    test('should multiply two numbers correctly', () => {
      expect(calculator.multiply(3, 4)).toBe(12);
      expect(calculator.multiply(-2, 3)).toBe(-6);
      expect(calculator.multiply(0, 5)).toBe(0);
    });

    test('should divide two numbers correctly', () => {
      expect(calculator.divide(10, 2)).toBe(5);
      expect(calculator.divide(7, 2)).toBe(3.5);
      expect(calculator.divide(-6, 3)).toBe(-2);
    });

    test('should throw error when dividing by zero', () => {
      expect(() => calculator.divide(5, 0)).toThrow('Division by zero is not allowed');
    });
  });

  describe('history functionality', () => {
    test('should record operations in history', () => {
      calculator.add(2, 3);
      calculator.multiply(4, 5);
      
      const history = calculator.getHistory();
      expect(history).toHaveLength(2);
      expect(history[0]).toBe('2 + 3 = 5');
      expect(history[1]).toBe('4 * 5 = 20');
    });

    test('should clear history', () => {
      calculator.add(1, 1);
      calculator.clearHistory();
      
      expect(calculator.getHistory()).toHaveLength(0);
    });

    test('should not modify original history array', () => {
      calculator.add(1, 1);
      const history1 = calculator.getHistory();
      const history2 = calculator.getHistory();
      
      expect(history1).toEqual(history2);
      expect(history1).not.toBe(history2); // Different references
    });
  });
});

src/tests/api.test.ts:

import { describe, test, expect, beforeEach, vi } from 'vitest';
import { UserApi, User } from '../api';

// Mock fetch globally
global.fetch = vi.fn();

describe('UserApi', () => {
  let api: UserApi;

  beforeEach(() => {
    api = new UserApi('https://api.example.com');
    vi.clearAllMocks();
  });

  describe('getUser', () => {
    test('should fetch user successfully', async () => {
      const mockUser: User = {
        id: 1,
        name: 'John Doe',
        email: '[email protected]'
      };

      (fetch as any).mockResolvedValueOnce({
        ok: true,
        json: vi.fn().mockResolvedValueOnce(mockUser),
      });

      const result = await api.getUser(1);

      expect(result).toEqual(mockUser);
      expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
    });

    test('should throw error when user not found', async () => {
      (fetch as any).mockResolvedValueOnce({
        ok: false,
        status: 404,
      });

      await expect(api.getUser(999)).rejects.toThrow('User with id 999 not found');
    });

    test('should throw error for other API errors', async () => {
      (fetch as any).mockResolvedValueOnce({
        ok: false,
        status: 500,
      });

      await expect(api.getUser(1)).rejects.toThrow('API error: 500');
    });
  });

  describe('createUser', () => {
    test('should create user successfully', async () => {
      const newUser = { name: 'Jane Doe', email: '[email protected]' };
      const createdUser: User = { id: 2, ...newUser };

      (fetch as any).mockResolvedValueOnce({
        ok: true,
        json: vi.fn().mockResolvedValueOnce(createdUser),
      });

      const result = await api.createUser(newUser);

      expect(result).toEqual(createdUser);
      expect(fetch).toHaveBeenCalledWith('https://api.example.com/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(newUser),
      });
    });

    test('should throw error when creation fails', async () => {
      (fetch as any).mockResolvedValueOnce({
        ok: false,
        status: 400,
      });

      const newUser = { name: 'Jane Doe', email: 'invalid-email' };
      await expect(api.createUser(newUser)).rejects.toThrow('Failed to create user: 400');
    });
  });
});

Integration testing with supertest:

import { describe, test, expect } from 'vitest';
import request from 'supertest';
import app from '../app'; // Your Express app

describe('API endpoints', () => {
  test('GET / returns hello message', async () => {
    const response = await request(app)
      .get('/')
      .expect(200);
    
    expect(response.body).toEqual({ message: 'Hello World!' });
  });

  test('POST /users creates user with valid data', async () => {
    const userData = {
      name: 'John Doe',
      email: '[email protected]',
      age: 30
    };

    const response = await request(app)
      .post('/users')
      .send(userData)
      .expect(201);

    expect(response.body.success).toBe(true);
    expect(response.body.user).toMatchObject(userData);
  });
});

Run tests:

npm test

Repository Links for Complete Projects:

  1. Express API Starter: github.com/microsoft/TypeScript-Node-Starter
  2. React TypeScript Template: github.com/facebook/create-react-app with --template typescript
  3. Full-Stack Examples: github.com/sindresorhus/awesome-nodejs

Next Steps

Intermediate Topics to Explore

  1. Advanced Async Patterns

    • AbortController for cancellable operations
    • Streaming APIs and async iterators
    • Worker threads for CPU-intensive tasks
    • Service Workers for background processing
  2. Advanced TypeScript

    • Template literal types
    • Conditional types and mapped types
    • Decorators (experimental)
    • Type guards and assertion functions
  3. Modern Framework Deep Dives

    • React: Suspense, Concurrent features, Server Components
    • Next.js: App Router, Server Actions
    • Vue 3: Composition API, Pinia
    • SvelteKit: Full-stack framework
  4. Backend Architecture

    • GraphQL with Apollo or Yoga
    • Database integration (Prisma, TypeORM)
    • Microservices patterns
    • Event-driven architecture
  5. Testing & Quality

    • End-to-end testing with Playwright
    • Visual regression testing
    • Property-based testing
    • Performance testing
  6. Build Tools & DevOps

    • Advanced Webpack/Vite configuration
    • Docker containerization
    • CI/CD with GitHub Actions
    • Deployment strategies
  7. Performance & Monitoring

    • Web Vitals optimization
    • Application monitoring (Sentry, DataDog)
    • Bundle analysis and optimization
    • Memory leak detection

Learning Path Recommendations

Month 1-2: 2025 Foundations

  • Master ES2025 features (Set methods, Promise.try, import attributes)
  • Learn TypeScript 5.5 (inferred type predicates, template literal improvements)
  • Set up modern tooling (Vite 6, ESLint 9 flat config, Vitest)
  • Build 2-3 small projects with modern patterns

Month 3-4: Framework Specialization

  • React 19 (Server Components, Actions API, useOptimistic)
  • Next.js 15 App Router (stable patterns)
  • Modern state management (Zustand, Jotai, or built-in React state)
  • Component architecture patterns

Month 5-6: Full-Stack Development

  • Node.js 22 LTS features (native WebSocket, enhanced ESM)
  • Database integration (Prisma 5+, tRPC)
  • Authentication systems (NextAuth.js, Clerk)
  • Deploy to modern platforms (Vercel, Railway, Fly.io)

Month 7-8: Advanced Concepts

  • Performance optimization (React Compiler, bundle analysis)
  • Advanced TypeScript patterns (template literal types, conditional types)
  • Testing strategies (Vitest, Playwright)
  • Open source contributions

Essential Resources

Documentation (Always Up-to-Date)

Learning Platforms

Practice & Challenges

Stay Current

Community & Help

Career Development

  • Build in public - share your learning journey
  • Contribute to open source projects
  • Attend meetups and conferences (virtual or in-person)
  • Keep a learning journal to track progress
  • Help others learn - teaching reinforces your own understanding

Building Your Portfolio

Essential Projects to Showcase:

  1. Todo App with Advanced Features (bit boring though... everyone has done it, but if you're a beginner sure give it a try I guess)

    • TypeScript, React/Vue, drag-and-drop, local storage
    • Live demo + clean code on GitHub
  2. Full-Stack Application

    • Express API, database, authentication, deployment
    • Real-world problem solver (weather app, expense tracker, etc.)
  3. Open Source Contribution

    • Bug fixes or feature additions to existing projects
    • Shows collaboration skills and code quality
  4. Performance-Focused Project

    • Demonstrates optimization techniques
    • Bundle analysis, lazy loading, caching strategies
  5. Testing Showcase

    • Well-tested codebase with multiple testing strategies
    • Shows professional development practices

Final Tips for Success

Development Mindset:

  • Write code every day - Consistency beats intensity
  • Build projects, not just tutorials - Apply what you learn immediately
  • Read other people's code - GitHub is a treasure trove of learning material
  • Embrace debugging - It's where real learning happens
  • Stay curious - Technology evolves rapidly; adaptability is key

Code Quality Habits:

  • Use TypeScript from day one on new projects
  • Set up ESLint and Prettier immediately
  • Write tests for critical functionality
  • Use meaningful commit messages
  • Review your own code before pushing

Learning Efficiency:

  • Focus on understanding concepts, not memorizing syntax
  • Practice explaining concepts to others (rubber duck debugging)
  • Join developer communities for support and networking
  • Set up a learning schedule and stick to it
  • Don't try to learn everything at once - master fundamentals first

Career Development:

  • Build in public - share your learning journey
  • Contribute to open source projects
  • Attend meetups and conferences (virtual or in-person)
  • Keep a learning journal to track progress
  • Help others learn - teaching reinforces your own understanding

Remember: Every expert was once a beginner. The JavaScript ecosystem can feel overwhelming, but with consistent practice and the right resources, you'll build the skills needed to create amazing applications.

The journey from beginner to proficient JavaScript developer typically takes 6-12 months of consistent practice. Focus on building real projects, understanding core concepts deeply, and staying current with modern best practices.

Most importantly: Start coding today, keep building, and never stop learning!


Last updated: July 2025 - Reflects ES2025 features, TypeScript 5.5, Node.js 22 LTS, React 19, and current 2025 best practices.

Advanced JavaScript/TypeScript Manual

Table of Contents

  1. Advanced Type System
  2. Memory Management & Performance
  3. Advanced Async Patterns
  4. Metaprogramming
  5. Advanced Object-Oriented Patterns
  6. Functional Programming Techniques
  7. Module Systems & Build Tools
  8. Security Considerations
  9. Advanced TypeScript Features
  10. Performance Optimization

Advanced Type System

Conditional Types

// Basic conditional type
type IsString<T> = T extends string ? true : false;
type Result1 = IsString<"hello">; // true
type Result2 = IsString<42>; // false

// Distributive conditional types
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>; // string[] | number[]

// Extracting function return types
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type FuncReturn = ReturnType<() => string>; // string

// Template literal types
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"

Mapped Types with Key Remapping

// Key remapping in mapped types
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

// Filtering properties
type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
};

type StringProps = PickByType<Person, string>; // { name: string }

Recursive Types

// JSON type definition
type JSONValue = 
  | string 
  | number 
  | boolean 
  | null
  | JSONObject
  | JSONArray;

interface JSONObject {
  [key: string]: JSONValue;
}

interface JSONArray extends Array<JSONValue> {}

// Deep readonly
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// Path types for nested objects
type Path<T> = T extends object ? {
  [K in keyof T]: K extends string ? K | `${K}.${Path<T[K]>}` : never;
}[keyof T] : never;

type PersonPaths = Path<{ user: { name: string; address: { city: string } } }>;
// "user" | "user.name" | "user.address" | "user.address.city"

Memory Management & Performance

WeakMap and WeakSet

// WeakMap for private properties
const privateData = new WeakMap();

class BankAccount {
  constructor(balance) {
    privateData.set(this, { balance });
  }
  
  getBalance() {
    return privateData.get(this).balance;
  }
}

// WeakSet for tracking objects without preventing GC
const processedObjects = new WeakSet();

function processOnce(obj) {
  if (processedObjects.has(obj)) {
    return;
  }
  processedObjects.add(obj);
  // Process object
}

Memory Leaks and Prevention

// Common leak: Event listeners
class Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }
  
  mount() {
    // Leak if not removed
    document.addEventListener('click', this.handleClick);
  }
  
  unmount() {
    // Prevent leak
    document.removeEventListener('click', this.handleClick);
  }
  
  handleClick(e) {
    // Handle click
  }
}

// Using AbortController for cleanup
function fetchWithCleanup(url) {
  const controller = new AbortController();
  
  const promise = fetch(url, { signal: controller.signal });
  
  // Return both promise and cleanup function
  return {
    promise,
    cleanup: () => controller.abort()
  };
}

Object Pooling

class ObjectPool {
  constructor(createFn, resetFn, maxSize = 100) {
    this.createFn = createFn;
    this.resetFn = resetFn;
    this.pool = [];
    this.maxSize = maxSize;
  }
  
  acquire() {
    if (this.pool.length > 0) {
      return this.pool.pop();
    }
    return this.createFn();
  }
  
  release(obj) {
    if (this.pool.length < this.maxSize) {
      this.resetFn(obj);
      this.pool.push(obj);
    }
  }
}

// Usage
const particlePool = new ObjectPool(
  () => ({ x: 0, y: 0, vx: 0, vy: 0 }),
  (p) => { p.x = 0; p.y = 0; p.vx = 0; p.vy = 0; },
  1000
);

Advanced Async Patterns

Async Iterators and Generators

// Async generator for paginated API
async function* fetchPages(baseUrl) {
  let page = 1;
  let hasMore = true;
  
  while (hasMore) {
    const response = await fetch(`${baseUrl}?page=${page}`);
    const data = await response.json();
    
    yield data.items;
    
    hasMore = data.hasNextPage;
    page++;
  }
}

// Consuming async iterator
async function getAllItems(url) {
  const items = [];
  
  for await (const pageItems of fetchPages(url)) {
    items.push(...pageItems);
  }
  
  return items;
}

// Transform stream with async generator
async function* transform(asyncIterable, transformFn) {
  for await (const item of asyncIterable) {
    yield await transformFn(item);
  }
}

Advanced Promise Patterns

// Promise with timeout
function withTimeout(promise, timeoutMs) {
  return Promise.race([
    promise,
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('Timeout')), timeoutMs)
    )
  ]);
}

// Retry with exponential backoff
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      const delay = baseDelay * Math.pow(2, i);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Concurrent execution with limit
async function mapWithConcurrencyLimit(items, asyncFn, limit) {
  const results = [];
  const executing = [];
  
  for (const [index, item] of items.entries()) {
    const promise = asyncFn(item).then(result => {
      results[index] = result;
    });
    
    executing.push(promise);
    
    if (executing.length >= limit) {
      await Promise.race(executing);
      executing.splice(executing.findIndex(p => p === promise), 1);
    }
  }
  
  await Promise.all(executing);
  return results;
}

Async Resource Management

// Async disposable pattern
interface AsyncDisposable {
  [Symbol.asyncDispose](): Promise<void>;
}

class DatabaseConnection implements AsyncDisposable {
  async connect() {
    // Connect to database
  }
  
  async [Symbol.asyncDispose]() {
    // Close connection
  }
}

// Usage with using syntax (TC39 proposal)
async function query() {
  await using db = new DatabaseConnection();
  await db.connect();
  // Use db
  // Automatically disposed when scope ends
}

Metaprogramming

Proxies for Advanced Patterns

// Observable object pattern
function observable(target) {
  const listeners = new Map();
  
  return new Proxy(target, {
    get(obj, prop) {
      if (prop === 'subscribe') {
        return (fn) => {
          if (!listeners.has(prop)) {
            listeners.set(prop, new Set());
          }
          listeners.get(prop).add(fn);
        };
      }
      return obj[prop];
    },
    
    set(obj, prop, value) {
      obj[prop] = value;
      
      if (listeners.has(prop)) {
        listeners.get(prop).forEach(fn => fn(value));
      }
      
      return true;
    }
  });
}

// Virtual properties
const lazyObject = new Proxy({}, {
  get(target, prop) {
    if (!(prop in target)) {
      target[prop] = expensiveComputation(prop);
    }
    return target[prop];
  }
});

// Method missing pattern
const dynamicAPI = new Proxy({}, {
  get(target, prop) {
    return (...args) => {
      return fetch(`/api/${prop}`, {
        method: 'POST',
        body: JSON.stringify(args)
      });
    };
  }
});

Reflect API

// Safe property access
function safeGet(target, propertyKey, receiver) {
  try {
    return Reflect.get(target, propertyKey, receiver);
  } catch (e) {
    return undefined;
  }
}

// Method decorator using Reflect
function memoize(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  const cache = new Map();
  
  descriptor.value = function(...args) {
    const key = JSON.stringify(args);
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    const result = Reflect.apply(originalMethod, this, args);
    cache.set(key, result);
    return result;
  };
  
  return descriptor;
}

Symbol Usage

// Private-like properties
const _private = Symbol('private');

class Example {
  constructor() {
    this[_private] = 'secret';
  }
  
  getPrivate() {
    return this[_private];
  }
}

// Protocol symbols
const dispose = Symbol.for('dispose');

class Resource {
  [dispose]() {
    // Cleanup
  }
}

// Custom iteration
const fibonacci = {
  [Symbol.iterator]() {
    let prev = 0, curr = 1;
    
    return {
      next() {
        [prev, curr] = [curr, prev + curr];
        return { value: curr, done: false };
      }
    };
  }
};

Advanced Object-Oriented Patterns

Mixins and Composition

// Mixin pattern
type Constructor<T = {}> = new (...args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

function Tagged<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    tags: string[] = [];
    
    addTag(tag: string) {
      this.tags.push(tag);
    }
  };
}

// Usage
class Article {
  constructor(public title: string) {}
}

class TimestampedTaggedArticle extends Tagged(Timestamped(Article)) {}

const article = new TimestampedTaggedArticle("My Article");
article.addTag("javascript");
console.log(article.timestamp);

Abstract Factory Pattern

// Abstract factory with type safety
interface Button {
  render(): void;
}

interface Input {
  render(): void;
}

abstract class UIFactory {
  abstract createButton(): Button;
  abstract createInput(): Input;
}

class MaterialUIFactory extends UIFactory {
  createButton(): Button {
    return new MaterialButton();
  }
  
  createInput(): Input {
    return new MaterialInput();
  }
}

// Factory registry
class UIFactoryRegistry {
  private static factories = new Map<string, Constructor<UIFactory>>();
  
  static register(name: string, factory: Constructor<UIFactory>) {
    this.factories.set(name, factory);
  }
  
  static create(name: string): UIFactory {
    const Factory = this.factories.get(name);
    if (!Factory) throw new Error(`Unknown factory: ${name}`);
    return new Factory();
  }
}

Visitor Pattern with Type Safety

// Type-safe visitor pattern
interface FileSystemNode {
  accept<T>(visitor: FileSystemVisitor<T>): T;
}

interface FileSystemVisitor<T> {
  visitFile(file: File): T;
  visitDirectory(directory: Directory): T;
}

class File implements FileSystemNode {
  constructor(public name: string, public size: number) {}
  
  accept<T>(visitor: FileSystemVisitor<T>): T {
    return visitor.visitFile(this);
  }
}

class Directory implements FileSystemNode {
  constructor(public name: string, public children: FileSystemNode[]) {}
  
  accept<T>(visitor: FileSystemVisitor<T>): T {
    return visitor.visitDirectory(this);
  }
}

// Size calculator visitor
class SizeCalculator implements FileSystemVisitor<number> {
  visitFile(file: File): number {
    return file.size;
  }
  
  visitDirectory(directory: Directory): number {
    return directory.children.reduce(
      (sum, child) => sum + child.accept(this), 
      0
    );
  }
}

Functional Programming Techniques

Advanced Composition

// Function composition with pipe
const pipe = (...fns) => (value) => 
  fns.reduce((acc, fn) => fn(acc), value);

// Async pipe
const asyncPipe = (...fns) => async (value) => {
  let result = value;
  for (const fn of fns) {
    result = await fn(result);
  }
  return result;
};

// Kleisli composition (monadic composition)
const composeK = (...fns) => (value) =>
  fns.reduce((chain, fn) => chain.then(fn), Promise.resolve(value));

// Transducers
const map = (fn) => (reducer) => (acc, value) => 
  reducer(acc, fn(value));

const filter = (predicate) => (reducer) => (acc, value) =>
  predicate(value) ? reducer(acc, value) : acc;

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

const transduce = (transducer, reducer, init, collection) =>
  collection.reduce(transducer(reducer), init);

Monadic Patterns

// Maybe monad
class Maybe<T> {
  constructor(private value: T | null) {}
  
  static of<T>(value: T | null): Maybe<T> {
    return new Maybe(value);
  }
  
  static none<T>(): Maybe<T> {
    return new Maybe<T>(null);
  }
  
  map<U>(fn: (value: T) => U): Maybe<U> {
    return this.value === null 
      ? Maybe.none<U>() 
      : Maybe.of(fn(this.value));
  }
  
  flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U> {
    return this.value === null 
      ? Maybe.none<U>() 
      : fn(this.value);
  }
  
  getOrElse(defaultValue: T): T {
    return this.value ?? defaultValue;
  }
}

// Result monad for error handling
class Result<T, E> {
  constructor(
    private value: T | null,
    private error: E | null
  ) {}
  
  static ok<T, E>(value: T): Result<T, E> {
    return new Result<T, E>(value, null);
  }
  
  static err<T, E>(error: E): Result<T, E> {
    return new Result<T, E>(null, error);
  }
  
  map<U>(fn: (value: T) => U): Result<U, E> {
    return this.value !== null
      ? Result.ok(fn(this.value))
      : Result.err(this.error!);
  }
  
  mapError<F>(fn: (error: E) => F): Result<T, F> {
    return this.error !== null
      ? Result.err(fn(this.error))
      : Result.ok(this.value!);
  }
  
  match<U>(handlers: { ok: (value: T) => U; err: (error: E) => U }): U {
    return this.value !== null
      ? handlers.ok(this.value)
      : handlers.err(this.error!);
  }
}

Lazy Evaluation

// Lazy sequences
class Lazy {
  constructor(iterable) {
    this.iterable = iterable;
  }
  
  *[Symbol.iterator]() {
    yield* this.iterable;
  }
  
  map(fn) {
    const iterable = this.iterable;
    return new Lazy(function* () {
      for (const value of iterable) {
        yield fn(value);
      }
    }());
  }
  
  filter(predicate) {
    const iterable = this.iterable;
    return new Lazy(function* () {
      for (const value of iterable) {
        if (predicate(value)) yield value;
      }
    }());
  }
  
  take(n) {
    const iterable = this.iterable;
    return new Lazy(function* () {
      let count = 0;
      for (const value of iterable) {
        if (count++ >= n) break;
        yield value;
      }
    }());
  }
  
  toArray() {
    return [...this];
  }
}

// Infinite sequences
function* naturals() {
  let n = 0;
  while (true) yield n++;
}

const firstTenEvens = new Lazy(naturals())
  .filter(n => n % 2 === 0)
  .take(10)
  .toArray();

Module Systems & Build Tools

Dynamic Imports and Code Splitting

// Dynamic import with loading states
async function loadComponent(name) {
  try {
    const module = await import(`./components/${name}.js`);
    return module.default;
  } catch (error) {
    console.error(`Failed to load component ${name}:`, error);
    return null;
  }
}

// Conditional loading based on feature detection
if ('IntersectionObserver' in window) {
  import('./lazy-load.js').then(module => {
    module.initLazyLoading();
  });
}

// Route-based code splitting
const routes = {
  '/': () => import('./pages/Home.js'),
  '/about': () => import('./pages/About.js'),
  '/dashboard': () => import('./pages/Dashboard.js')
};

async function navigate(path) {
  const loader = routes[path];
  if (loader) {
    const module = await loader();
    module.default.render();
  }
}

Module Federation

// Webpack module federation configuration
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
        app2: 'app2@http://localhost:3002/remoteEntry.js',
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true }
      }
    })
  ]
};

// Runtime remote loading
async function loadRemoteModule(scope, module) {
  await __webpack_init_sharing__('default');
  const container = window[scope];
  await container.init(__webpack_share_scopes__.default);
  const factory = await container.get(module);
  return factory();
}

Security Considerations

Content Security Policy

// CSP header generation
function generateCSP(policies) {
  return Object.entries(policies)
    .map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
    .join('; ');
}

const csp = generateCSP({
  'default-src': ["'self'"],
  'script-src': ["'self'", "'nonce-${nonce}'", 'https://trusted-cdn.com'],
  'style-src': ["'self'", "'unsafe-inline'"],
  'img-src': ["'self'", 'data:', 'https:'],
  'connect-src': ["'self'", 'https://api.example.com']
});

// Nonce generation for inline scripts
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');

XSS Prevention

// Safe HTML templating
class SafeHTML {
  constructor(strings, ...values) {
    this.strings = strings;
    this.values = values;
  }
  
  toString() {
    return this.strings.reduce((result, str, i) => {
      const value = this.values[i - 1];
      const escaped = typeof value === 'string' 
        ? this.escapeHtml(value) 
        : value;
      return result + escaped + str;
    });
  }
  
  escapeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
  }
}

// Tagged template for safe HTML
function html(strings, ...values) {
  return new SafeHTML(strings, ...values);
}

// Usage
const userInput = '<script>alert("XSS")</script>';
const safe = html`<div>${userInput}</div>`;

CSRF Protection

// CSRF token management
class CSRFTokenManager {
  constructor() {
    this.token = this.generateToken();
  }
  
  generateToken() {
    return Array.from(crypto.getRandomValues(new Uint8Array(32)))
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
  
  attachToRequest(request) {
    request.headers.set('X-CSRF-Token', this.token);
    return request;
  }
  
  validateToken(token) {
    return crypto.subtle.digest('SHA-256', 
      new TextEncoder().encode(token)
    ).then(hash => {
      const expected = crypto.subtle.digest('SHA-256', 
        new TextEncoder().encode(this.token)
      );
      return hash === expected;
    });
  }
}

// Double submit cookie pattern
function setCSRFCookie(token) {
  document.cookie = `csrf=${token}; SameSite=Strict; Secure`;
}

Advanced TypeScript Features

Type Inference and Narrowing

// Discriminated unions with exhaustive checking
type Result<T, E> = 
  | { kind: 'success'; value: T }
  | { kind: 'error'; error: E };

function assertNever(x: never): never {
  throw new Error('Unexpected object: ' + x);
}

function handleResult<T, E>(result: Result<T, E>) {
  switch (result.kind) {
    case 'success':
      return result.value;
    case 'error':
      throw result.error;
    default:
      return assertNever(result);
  }
}

// Type predicates
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

// Assertion functions
function assertDefined<T>(value: T | undefined): asserts value is T {
  if (value === undefined) {
    throw new Error('Value is undefined');
  }
}

// Using assertion functions
function processValue(value: string | undefined) {
  assertDefined(value);
  // TypeScript knows value is string here
  return value.toUpperCase();
}

Advanced Generics

// Higher-kinded types simulation
interface HKT<URI, A> {
  _URI: URI;
  _A: A;
}

interface Functor<F> {
  map<A, B>(fa: HKT<F, A>, f: (a: A) => B): HKT<F, B>;
}

// Variance annotations
interface Producer<out T> {
  produce(): T;
}

interface Consumer<in T> {
  consume(value: T): void;
}

// Generic constraints with conditional types
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;

type DeepFlatten<T> = T extends Array<infer U> 
  ? DeepFlatten<U>
  : T extends object 
    ? { [K in keyof T]: DeepFlatten<T[K]> }
    : T;

// Builder pattern with type state
interface EmailBuilder<T> {
  from(address: string): EmailBuilder<T & { from: string }>;
  to(address: string): EmailBuilder<T & { to: string }>;
  subject(text: string): EmailBuilder<T & { subject: string }>;
  body(text: string): EmailBuilder<T & { body: string }>;
}

type CompleteEmail = { from: string; to: string; subject: string; body: string };

interface EmailBuilderClass<T> extends EmailBuilder<T> {
  send(this: EmailBuilder<CompleteEmail>): void;
}

Declaration Merging and Module Augmentation

// Module augmentation
declare module 'express' {
  interface Request {
    user?: { id: string; name: string };
  }
}

// Global augmentation
declare global {
  interface Window {
    myAPI: {
      doSomething(): void;
    };
  }
}

// Namespace merging
class Album {
  label: Album.AlbumLabel;
}

namespace Album {
  export class AlbumLabel {
    constructor(public name: string) {}
  }
}

// Interface merging for plugins
interface PluginSystem {
  use(plugin: Plugin): void;
}

interface Plugin {
  name: string;
  install(system: PluginSystem): void;
}

interface PluginSystem {
  // Additional methods can be added by plugins
  [key: string]: any;
}

Performance Optimization

Web Workers for CPU-Intensive Tasks

// Main thread
class WorkerPool {
  constructor(workerScript, poolSize = navigator.hardwareConcurrency) {
    this.workers = [];
    this.queue = [];
    
    for (let i = 0; i < poolSize; i++) {
      const worker = new Worker(workerScript);
      worker.onmessage = this.handleMessage.bind(this, worker);
      this.workers.push({ worker, busy: false });
    }
  }
  
  async execute(data) {
    return new Promise((resolve, reject) => {
      const availableWorker = this.workers.find(w => !w.busy);
      
      if (availableWorker) {
        this.runTask(availableWorker, { data, resolve, reject });
      } else {
        this.queue.push({ data, resolve, reject });
      }
    });
  }
  
  runTask(workerInfo, task) {
    workerInfo.busy = true;
    workerInfo.currentTask = task;
    workerInfo.worker.postMessage(task.data);
  }
  
  handleMessage(worker, event) {
    const workerInfo = this.workers.find(w => w.worker === worker);
    const { resolve } = workerInfo.currentTask;
    
    resolve(event.data);
    workerInfo.busy = false;
    
    if (this.queue.length > 0) {
      const nextTask = this.queue.shift();
      this.runTask(workerInfo, nextTask);
    }
  }
}

// Worker script
self.onmessage = async function(event) {
  const result = await processData(event.data);
  self.postMessage(result);
};

Memory-Efficient Data Structures

// Bit vector for space-efficient boolean arrays
class BitVector {
  constructor(size) {
    this.size = size;
    this.data = new Uint32Array(Math.ceil(size / 32));
  }
  
  set(index, value) {
    const wordIndex = Math.floor(index / 32);
    const bitIndex = index % 32;
    
    if (value) {
      this.data[wordIndex] |= (1 << bitIndex);
    } else {
      this.data[wordIndex] &= ~(1 << bitIndex);
    }
  }
  
  get(index) {
    const wordIndex = Math.floor(index / 32);
    const bitIndex = index % 32;
    return (this.data[wordIndex] & (1 << bitIndex)) !== 0;
  }
}

// Trie for efficient string storage and lookup
class Trie {
  constructor() {
    this.root = {};
  }
  
  insert(word) {
    let node = this.root;
    for (const char of word) {
      if (!node[char]) {
        node[char] = {};
      }
      node = node[char];
    }
    node.isEnd = true;
  }
  
  search(word) {
    let node = this.root;
    for (const char of word) {
      if (!node[char]) return false;
      node = node[char];
    }
    return node.isEnd === true;
  }
  
  startsWith(prefix) {
    let node = this.root;
    for (const char of prefix) {
      if (!node[char]) return false;
      node = node[char];
    }
    return true;
  }
}

Optimization Techniques

// Debouncing and throttling
function debounce(fn, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), delay);
  };
}

function throttle(fn, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      fn.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// Request idle callback for non-critical work
function scheduleIdleWork(tasks) {
  let taskIndex = 0;
  
  function workLoop(deadline) {
    while (taskIndex < tasks.length && deadline.timeRemaining() > 0) {
      tasks[taskIndex++]();
    }
    
    if (taskIndex < tasks.length) {
      requestIdleCallback(workLoop);
    }
  }
  
  requestIdleCallback(workLoop);
}

// Virtual scrolling implementation
class VirtualScroller {
  constructor(container, items, itemHeight, renderFn) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.renderFn = renderFn;
    
    this.setupContainer();
    this.render();
    
    this.container.addEventListener('scroll', 
      throttle(() => this.render(), 16)
    );
  }
  
  setupContainer() {
    const totalHeight = this.items.length * this.itemHeight;
    const scroller = document.createElement('div');
    scroller.style.height = `${totalHeight}px`;
    this.container.appendChild(scroller);
  }
  
  render() {
    const scrollTop = this.container.scrollTop;
    const containerHeight = this.container.clientHeight;
    
    const startIndex = Math.floor(scrollTop / this.itemHeight);
    const endIndex = Math.ceil((scrollTop + containerHeight) / this.itemHeight);
    
    const visibleItems = this.items.slice(startIndex, endIndex);
    
    // Clear and render only visible items
    this.container.innerHTML = '';
    visibleItems.forEach((item, index) => {
      const element = this.renderFn(item);
      element.style.position = 'absolute';
      element.style.top = `${(startIndex + index) * this.itemHeight}px`;
      this.container.appendChild(element);
    });
  }
}

Best Practices Summary

Type Safety

  • Use strict TypeScript configuration
  • Prefer unknown over any
  • Use discriminated unions for state management
  • Implement exhaustive type checking

Performance

  • Profile before optimizing
  • Use Web Workers for CPU-intensive tasks
  • Implement virtual scrolling for large lists
  • Consider memory pooling for frequent allocations

Security

  • Always validate and sanitize user input
  • Use Content Security Policy
  • Implement proper CSRF protection
  • Avoid eval and Function constructor

Code Organization

  • Use module boundaries effectively
  • Implement dependency injection
  • Separate concerns with proper patterns
  • Document complex type definitions

Testing

  • Test edge cases and error paths
  • Use property-based testing for complex logic
  • Mock external dependencies properly
  • Measure performance in tests

This manual covers advanced concepts that are crucial for building robust, performant, and maintainable JavaScript/TypeScript applications. Continue exploring and experimenting with these patterns to deepen your understanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment