InterviewPitch
TypeScript interview questions

TypeScript Interview Questions with Answers

Most Asked TypeScript Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

TypeScript is a strongly typed programming language built on JavaScript that brings static typing, modern ES6+ features, and excellent tooling to the world of web development. Developed and maintained by Microsoft, TypeScript is the language of choice for large-scale applications, used by companies like Google, Microsoft, Airbnb, and countless others. This comprehensive guide presents 100+ carefully curated TypeScript interview questions and answers, covering everything from the basics to advanced type system features. You'll master interfaces, type aliases, union/intersection types, generics, classes, access modifiers, decorators, utility types (Partial, Required, Readonly, Pick, Omit, Record, and more), mapped types, conditional types, template literal types, type guards, and real-world coding problems. Whether you're preparing for a frontend role with React or Angular, a full-stack position, or a backend Node.js job, this question bank will solidify your understanding of TypeScript and give you the confidence to ace your interview. Start practicing now and become a TypeScript expert.

Why TypeScript?

  • Strong static typing catches errors at compile time, reducing runtime bugs
  • Excellent tooling and IDE support – autocompletion, navigation, and refactoring
  • Superset of JavaScript – works seamlessly with all existing JavaScript libraries
  • Used by major frameworks like React, Angular, and Vue – essential for large-scale apps
  • Enables scalable and maintainable enterprise-grade applications
  • Growing community, continuous improvements, and high demand in the job market

Most Asked TypeScript Interview Questions

Beginner
1. What is TypeScript?

TypeScript is a strongly typed, object-oriented, compiled programming language built on JavaScript. It adds static types to JavaScript.

  • Static typing: Type checking at compile time
  • ES6+ features: Supports modern JavaScript
  • Object-oriented: Classes, interfaces, inheritance
  • Tooling: Better IDE support and autocompletion
  • Compiles to JavaScript: Runs anywhere JavaScript runs
typescript
// Hello World in TypeScript
console.log("Hello, World!");

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

console.log(greet("TypeScript"));
Beginner
2. How to declare variables in TypeScript?

Variables in TypeScript are declared with let (mutable) and const (immutable), with optional type annotations.

  • let: Mutable variable
  • const: Immutable constant
  • Type inference: Types are inferred automatically
  • Type annotations: let name: string = "Alice"
  • Primitive types: string, number, boolean, null, undefined
typescript
// Variables in TypeScript
// Immutable variable (const)
const immutableVar = "World";

// Mutable variable (let)
let mutableVar = "Hello";
mutableVar = "TypeScript";

// Type inference
let inferred = 42; // TypeScript infers 'number'

// Explicit type annotation
let explicit: number = 10;

// Type annotations
let name: string = "Alice";
let age: number = 25;
let isActive: boolean = true;
let numbers: number[] = [1, 2, 3];
let tuple: [string, number] = ["Alice", 25];
let anyValue: any = "anything";
let unknownValue: unknown = 42;
let voidValue: void = undefined;
let nullValue: null = null;
let undefinedValue: undefined = undefined;

// Display
console.log(immutableVar);
console.log(mutableVar);
console.log(inferred);
console.log(explicit);
Beginner
3. What are the data types in TypeScript?

TypeScript provides primitive types, object types, union types, intersection types, literal types, and more.

  • Primitive: string, number, boolean, null, undefined, symbol, bigint
  • Array: number[] or Array<number>
  • Tuple: [string, number]
  • Object: { name: string; age: number }
  • Union: string | number
  • Intersection: Name & Age
  • Literal: "active" | "inactive"
  • Void: void
  • Never: never
typescript
// TypeScript Data Types
// Primitive types
let name: string = "Alice";
let age: number = 25;
let isActive: boolean = true;
let nullable: null = null;
let undefinedValue: undefined = undefined;
let bigNumber: bigint = 100n;
let uniqueSymbol: symbol = Symbol("id");

// Array types
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];

// Tuple types
let user: [string, number] = ["Alice", 25];

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

// Union types
let id: string | number = "123";
id = 456;

// Intersection types
interface Name {
  name: string;
}
interface Age {
  age: number;
}
type Person = Name & Age;
let alice: Person = { name: "Alice", age: 25 };

// Literal types
type Status = "active" | "inactive" | "pending";
let status: Status = "active";

// Void type (functions that don't return)
function logMessage(message: string): void {
  console.log(message);
}

// Never type (functions that never return)
function throwError(message: string): never {
  throw new Error(message);
}

// Type assertions
let someValue: any = "Hello";
let strLength: number = (someValue as string).length;
let strLength2: number = (<string>someValue).length;

// Optional types
interface User {
  name: string;
  age?: number; // Optional
}

// Readonly types
interface ReadonlyUser {
  readonly id: number;
  name: string;
}

// Type aliases
type UserId = string | number;
type Callback = (data: any) => void;

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

let result = identity<string>("Hello");

// Utility types
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type ReadonlyUser2 = Readonly<User>;
type UserKeys = keyof User;
type UserName = Pick<User, "name">;
type UserWithoutAge = Omit<User, "age">;

// Examples
console.log(name, age, isActive);
console.log(numbers, strings);
console.log(user);
console.log(person);
console.log(id);
console.log(alice);
console.log(status);
logMessage("Hello");
Beginner
4. How to define functions in TypeScript?

Functions in TypeScript include type annotations for parameters and return values, supporting optional and default parameters.

  • Function: function name(params): returnType { }
  • Arrow function: (params): returnType => { }
  • Optional params: name?: string
  • Default params: name: string = "Guest"
  • Rest params: ...numbers: number[]
  • Function overloads: Multiple signatures
typescript
// TypeScript Functions

// Basic function with type annotations
function greet(name: string): string {
  return `Hello, ${name}!`;
}

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

// Optional parameters
function greetOptional(name: string, age?: number): string {
  if (age) {
    return `Hello, ${name}! You are ${age} years old.`;
  }
  return `Hello, ${name}!`;
}

// Default parameters
function greetDefault(name: string = "Guest"): string {
  return `Hello, ${name}!`;
}

// Rest parameters
function sumAll(...numbers: number[]): number {
  return numbers.reduce((sum, num) => sum + num, 0);
}

// Function with object parameter
function printUser(user: { name: string; age: number }): void {
  console.log(`Name: ${user.name}, Age: ${user.age}`);
}

// Function with interface
interface User {
  name: string;
  age: number;
  email?: string;
}

function createUser(user: User): User {
  return {
    name: user.name,
    age: user.age,
    email: user.email || "no-email@example.com"
  };
}

// Function overloads
function getData(id: number): string;
function getData(id: string): number;
function getData(id: number | string): string | number {
  if (typeof id === "number") {
    return `User ID: ${id}`;
  } else {
    return id.length;
  }
}

// Void return type
function logMessage(message: string): void {
  console.log(message);
}

// Never return type (function that throws error)
function throwError(message: string): never {
  throw new Error(message);
}

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

// Generic with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// Function type
type MathOperation = (a: number, b: number) => number;

const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;

// Higher-order function
function createMultiplier(factor: number): (value: number) => number {
  return (value: number) => value * factor;
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

// Async function
async function fetchData(): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Data fetched"), 1000);
  });
}

// Examples
console.log(greet("Alice"));
console.log(greetArrow("Bob"));
console.log(greetOptional("Charlie", 30));
console.log(greetDefault());
console.log(sumAll(1, 2, 3, 4, 5));

const user = createUser({ name: "David", age: 25 });
console.log(user);

console.log(getData(123)); // "User ID: 123"
console.log(getData("hello")); // 5

console.log(add(5, 3)); // 8
console.log(double(5)); // 10
console.log(triple(5)); // 15

// Using generic function
console.log(identity<string>("Hello"));
console.log(identity<number>(42));

// Using getProperty
const person = { name: "Alice", age: 25, city: "NYC" };
console.log(getProperty(person, "name")); // "Alice"
Beginner
5. What are interfaces in TypeScript?

Interfaces define the structure of an object. They are used for type-checking and can be extended or implemented.

  • Declaration: interface User { name: string; age: number; }
  • Optional properties: email?: string
  • Readonly properties: readonly id: number
  • Function types: (param: string): void
  • Index signatures: [key: string]: any
typescript
// Arrays in TypeScript
// Array creation
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["Apple", "Banana", "Orange"];
let mixed: (string | number)[] = [1, "Hello", 3.14];

// Generic array
let genericNumbers: Array<number> = [1, 2, 3];

// Readonly array
let readonlyArray: readonly number[] = [1, 2, 3];

// Access and modify
console.log(numbers[2]);
numbers[2] = 10;

// Array operations
console.log(numbers.length);
numbers.push(6);
numbers.pop();

// Iteration
for (const num of numbers) {
    console.log(num);
}

// Array methods with types
const doubled: number[] = numbers.map((num: number): number => num * 2);
const filtered: number[] = numbers.filter((num: number): boolean => num > 2);
const sum: number = numbers.reduce((acc: number, num: number): number => acc + num, 0);

console.log(doubled);
console.log(filtered);
console.log(sum);

// Empty arrays with type
let emptyArray: number[] = [];
let anotherEmpty = new Array<number>();
Beginner
6. What is the difference between type aliases and interfaces?

Type aliases are named types that can be used for primitives, unions, intersections, and tuples. Interfaces are limited to object shapes and can be extended/merged.

  • Type: type Name = string, type Status = "active" | "inactive"
  • Interface: interface User { name: string }
  • Extending: interfaces extend with extends, types use intersection &
  • Declaration merging: interfaces support it, types do not
  • When to use: interface for object shapes, type for everything else
typescript
// Collections in TypeScript
// Array (ordered, allows duplicates)
const immutableArray: number[] = [1, 2, 3, 4, 5];
let mutableArray: number[] = [1, 2, 3];
mutableArray.push(4);
mutableArray.splice(1, 1);

// Set (unordered, unique values)
const immutableSet: Set<number> = new Set([1, 2, 3]);
let mutableSet: Set<number> = new Set([1, 2, 3]);
mutableSet.add(4);
mutableSet.delete(2);

// Map (key-value pairs)
const immutableMap: Map<string, string> = new Map([["key1", "value1"], ["key2", "value2"]]);
let mutableMap: Map<string, string> = new Map([["key1", "value1"]]);
mutableMap.set("key2", "value2");
mutableMap.delete("key1");

// Collection operations with types
const numbers: number[] = [1, 2, 3, 4, 5, 6];
const evens: number[] = numbers.filter((num: number): boolean => num % 2 === 0);
const doubled: number[] = numbers.map((num: number): number => num * 2);
const sum: number = numbers.reduce((acc: number, num: number): number => acc + num, 0);

console.log(evens);
console.log(doubled);
console.log(sum);

// Type assertions for collections
const stringSet: Set<string> = new Set(["a", "b", "c"]);
const numberMap: Map<number, string> = new Map([[1, "one"], [2, "two"]]);
Beginner
7. What are union and intersection types in TypeScript?

Union types allow a value to be one of several types. Intersection types combine multiple types into one.

  • Union: string | number
  • Intersection: A & B
  • Type guards: narrow union types with typeof or instanceof
  • Discriminated unions: use a literal property to discriminate
  • Intersection for mixins: combine object types
typescript
// Classes and Interfaces in TypeScript
// Interface definition
interface Person {
    name: string;
    age: number;
    city?: string; // Optional property
    readonly id: number; // Readonly property
    greet(): string;
}

// Class implementing interface
class Person implements Person {
    readonly id: number;
    name: string;
    age: number;
    city: string;
    
    constructor(id: number, name: string, age: number, city: string = "Unknown") {
        this.id = id;
        this.name = name;
        this.age = age;
        this.city = city;
    }
    
    greet(): string {
        return `Hello, my name is ${this.name}`;
    }
    
    // Method with type
    updateAge(newAge: number): void {
        this.age = newAge;
    }
}

// Abstract class
abstract class Animal {
    abstract name: string;
    
    abstract makeSound(): string;
    
    move(): string {
        return "Moving...";
    }
}

class Dog extends Animal {
    name: string;
    breed: string;
    
    constructor(name: string, breed: string) {
        super();
        this.name = name;
        this.breed = breed;
    }
    
    makeSound(): string {
        return "Woof!";
    }
}

// Usage
const person = new Person(1, "Alice", 25, "NYC");
console.log(person.greet());
console.log(person.id); // Readonly

const dog = new Dog("Rex", "German Shepherd");
console.log(dog.makeSound());
console.log(dog.move());
Beginner
8. What are type assertions in TypeScript?

Type assertions inform the compiler about the type of a value when you know more than it does. They do not affect runtime.

  • as syntax: value as string
  • Angle bracket: <string>value (not allowed in JSX)
  • Non-null assertion: value!
  • Double assertion: value as any as string (use sparingly)
  • Const assertion: as const for literal types
typescript
// Enums in TypeScript
// Numeric enum
enum Status {
    Pending = 1,
    Active,
    Inactive,
    Suspended
}

// String enum
enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE"
}

// Enum with methods
enum Payment {
    Cash = 100,
    CreditCard = 200,
    PayPal = 300
}

// Heterogeneous enum
enum Mixed {
    No = 0,
    Yes = "YES"
}

// Enum as type
let status: Status = Status.Active;
let color: Color = Color.Red;

// Enum usage
function handleStatus(status: Status): string {
    switch (status) {
        case Status.Pending:
            return "Pending...";
        case Status.Active:
            return "Active";
        case Status.Inactive:
            return "Inactive";
        case Status.Suspended:
            return "Suspended";
        default:
            return "Unknown";
    }
}

console.log(handleStatus(Status.Active));
console.log(Color.Red);
console.log(Payment.Cash);

// Const enum (inlined)
const enum ConstEnum {
    A = 1,
    B = 2
}
console.log(ConstEnum.A);

// Enum with computed values
enum Computed {
    A = 1,
    B = A * 2,
    C = B * 2
}
Beginner
9. What are type guards in TypeScript?

Type guards are expressions that perform runtime checks and narrow the type of a variable within a block.

  • typeof: if (typeof value === "string")
  • instanceof: if (value instanceof Date)
  • Custom type predicate: function isString(value: any): value is string
  • in operator: if ("name" in obj)
  • Discriminated union: check common property
typescript
// Null Safety in TypeScript
// Optional types
let nullableString: string | null = "Hello";
let optionalString: string | undefined = "World";
let maybeNumber: number | null | undefined = 42;

// Optional chaining
interface User {
    name: string;
    address?: {
        city: string;
        zip?: string;
    };
}

const user: User = { name: "Alice" };

// Safe access with optional chaining
const city = user.address?.city ?? "Unknown";
console.log(city);

// Nullish coalescing
const value = null ?? "default";
console.log(value);

// Type guard
function isString(value: any): value is string {
    return typeof value === "string";
}

// Using type guard
function processValue(value: string | number): string {
    if (isString(value)) {
        return `String: ${value}`;
    }
    return `Number: ${value}`;
}

// Non-null assertion operator
let maybeString: string | null = "hello";
const definitelyString = maybeString!;

// Optional parameters in functions
function greet(name?: string): string {
    return `Hello, ${name ?? "Guest"}`;
}

console.log(greet());
console.log(greet("Alice"));
Beginner
10. What are generics in TypeScript?

Generics allow creating reusable components that work with a variety of types while maintaining type safety.

  • Generic function: function identity<T>(arg: T): T
  • Generic class: class Box<T> { value: T }
  • Generic constraints: <T extends HasName>
  • Multiple type parameters: <K, V>
  • Default types: <T = string>
typescript
// Control Flow in TypeScript
// If-else
const age: number = 25;
const status: string = age < 18 ? "Minor" : "Adult";
console.log(status);

// If-else-if
const grade: string = "A";
let result: string;
if (grade === "A") {
    result = "Excellent";
} else if (grade === "B") {
    result = "Good";
} else if (grade === "C") {
    result = "Fair";
} else {
    result = "Needs Improvement";
}
console.log(result);

// Switch statement
const score: number = 85;
let grade2: string;
switch (true) {
    case score >= 90:
        grade2 = "A";
        break;
    case score >= 80:
        grade2 = "B";
        break;
    case score >= 70:
        grade2 = "C";
        break;
    default:
        grade2 = "F";
}
console.log(grade2);

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

// For-of loop
const items: string[] = ["A", "B", "C"];
for (const item of items) {
    console.log(item);
}

// For-in loop
const obj: { [key: string]: string } = { a: "A", b: "B" };
for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(`${key}: ${obj[key]}`);
    }
}

// While loop
let i: number = 0;
while (i < 5) {
    console.log(i);
    i++;
}

// Do-while loop
i = 0;
do {
    console.log(i);
    i--;
} while (i > 0);

// For loop with type guard
const mixedArray: (string | number)[] = [1, "two", 3, "four"];
for (const item of mixedArray) {
    if (typeof item === "string") {
        console.log(`String: ${item}`);
    } else {
        console.log(`Number: ${item}`);
    }
}
Beginner
11. How to define classes in TypeScript?

Classes in TypeScript extend ES6 classes with type annotations and access modifiers.

  • Class: class Person { name: string; constructor(name: string) { this.name = name; } }
  • Access modifiers: public, private, protected
  • Readonly: readonly id: number
  • Abstract classes: abstract class Animal
  • Implements: class Student implements Person
typescript
// Inheritance and Polymorphism in TypeScript
// Base class
class Animal {
    constructor(public name: string) {}
    
    makeSound(): string {
        return "Animal sound";
    }
}

// Derived class
class Dog extends Animal {
    constructor(name: string, public breed: string) {
        super(name);
    }
    
    override makeSound(): string {
        return "Woof!";
    }
}

// Abstract class
abstract class Vehicle {
    constructor(public brand: string) {}
    
    abstract start(): string;
    
    stop(): string {
        return "Stopped";
    }
}

class Car extends Vehicle {
    constructor(brand: string, public model: string) {
        super(brand);
    }
    
    start(): string {
        return `${this.brand} ${this.model} started`;
    }
}

// Interface inheritance
interface Flyable {
    fly(): string;
}

interface Swimmable {
    swim(): string;
}

class Duck implements Flyable, Swimmable {
    fly(): string {
        return "Flying";
    }
    
    swim(): string {
        return "Swimming";
    }
}

// Polymorphism
function makeSound(animal: Animal): string {
    return animal.makeSound();
}

// Usage
const dog = new Dog("Rex", "German Shepherd");
console.log(dog.makeSound());
console.log(dog.breed);

const car = new Car("Toyota", "Camry");
console.log(car.start());
console.log(car.stop());

const duck = new Duck();
console.log(duck.fly());
console.log(duck.swim());

console.log(makeSound(dog));
Beginner
12. What are access modifiers in TypeScript?

Access modifiers control the visibility of class members. TypeScript provides public, private, protected, and readonly.

  • public: Accessible anywhere (default)
  • private: Accessible only within the class
  • protected: Accessible within the class and subclasses
  • readonly: Can only be set at declaration or in constructor
  • Parameter properties: constructor(private name: string)
typescript
// Properties and Accessors in TypeScript
class Person {
    private _name: string;
    private _age: number;
    private _email: string;
    
    constructor(name: string, age: number, email: string) {
        this._name = name;
        this._age = age;
        this._email = email;
    }
    
    // Getter
    get name(): string {
        return this._name.toUpperCase();
    }
    
    // Setter with validation
    set name(value: string) {
        this._name = value.trim();
    }
    
    get age(): number {
        return this._age;
    }
    
    set age(value: number) {
        if (value >= 0) {
            this._age = value;
        }
    }
    
    // Read-only property
    get email(): string {
        return this._email;
    }
    
    // Computed property
    get fullName(): string {
        return `${this._name} (Age: ${this._age})`;
    }
}

// Property with lazy initialization
class LazyProperty {
    private _expensiveData: string | null = null;
    
    get expensiveData(): string {
        if (this._expensiveData === null) {
            console.log("Computing expensive data...");
            this._expensiveData = "Expensive Result";
        }
        return this._expensiveData;
    }
}

// Usage
const person = new Person("  Alice  ", 25, "alice@example.com");
console.log(person.name); // ALICE
person.name = "Bob";
console.log(person.name); // BOB
person.age = 26;
console.log(person.age);
console.log(person.email);
console.log(person.fullName);

const lazy = new LazyProperty();
console.log(lazy.expensiveData); // Computes
console.log(lazy.expensiveData); // Returns cached
Beginner
13. What are abstract classes in TypeScript?

Abstract classes cannot be instantiated directly. They are designed to be extended by subclasses and can contain abstract methods.

  • Abstract class: abstract class Vehicle { abstract start(): void; stop(): void { ... } }
  • Abstract methods: have no implementation
  • Concrete methods: can be used as is
  • Constructors: can be called via super()
  • Use case: define a common interface for subclasses
typescript
// Static Members in TypeScript
class MyClass {
    // Static property
    static counter: number = 0;
    static readonly TAG: string = "MyClass";
    
    // Instance property
    id: number;
    
    constructor() {
        this.id = ++MyClass.counter;
    }
    
    // Static method
    static classMethod(): string {
        return `Class method called, counter: ${MyClass.counter}`;
    }
    
    // Static factory method
    static create(): MyClass {
        return new MyClass();
    }
    
    // Instance method
    instanceMethod(): string {
        return `Instance ${this.id} method called`;
    }
}

// Singleton pattern
class Singleton {
    private static instance: Singleton;
    private data: string[] = [];
    
    private constructor() {}
    
    static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
    
    addData(item: string): void {
        this.data.push(item);
    }
    
    getData(): string[] {
        return this.data;
    }
}

// Usage
console.log(MyClass.TAG);
const obj1 = MyClass.create();
const obj2 = MyClass.create();
console.log(MyClass.classMethod());
console.log(obj1.instanceMethod());
console.log(obj2.instanceMethod());

const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
singleton1.addData("Hello");
console.log(singleton2.getData()); // ["Hello"]
Beginner
14. How to use modules in TypeScript?

TypeScript supports ES modules with import and export syntax, along with namespace and module resolution options.

  • Export: export const PI = 3.14; export function add() { }
  • Import: import { PI, add } from "./math"
  • Default export: export default class Calculator
  • Namespace: namespace MyLib { export function do() { } }
  • Module resolution: Node, classic, etc.
typescript
// Exception Handling in TypeScript
// Custom error class
class ValidationError extends Error {
    constructor(message: string, public field: string) {
        super(message);
        this.name = "ValidationError";
    }
}

// Function that throws
function validateAge(age: number): void {
    if (age < 0) {
        throw new ValidationError("Age cannot be negative", "age");
    }
    if (age > 150) {
        throw new ValidationError("Invalid age", "age");
    }
}

// Function with try-catch
function divide(a: number, b: number): number {
    try {
        if (b === 0) {
            throw new Error("Division by zero");
        }
        return a / b;
    } catch (error) {
        if (error instanceof Error) {
            console.log(`Error: ${error.message}`);
        }
        return 0;
    }
}

// Async error handling
async function fetchData(): Promise<string> {
    try {
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return await response.text();
    } catch (error) {
        console.error("Fetch error:", error);
        throw error;
    }
}

// Usage
try {
    validateAge(25);
    console.log("Age is valid");
} catch (error) {
    if (error instanceof ValidationError) {
        console.log(`Validation error: ${error.message} (field: ${error.field})`);
    }
}

console.log(divide(10, 2));
console.log(divide(10, 0));

// try-catch-finally
try {
    console.log("Trying...");
    throw new Error("Error occurred");
} catch (error) {
    console.log("Caught error:", error);
} finally {
    console.log("Finally block executed");
}

// Result type pattern
type Result<T, E = Error> = 
    | { success: true; data: T }
    | { success: false; error: E };

function safeDivide(a: number, b: number): Result<number> {
    try {
        if (b === 0) {
            return { success: false, error: new Error("Division by zero") };
        }
        return { success: true, data: a / b };
    } catch (error) {
        return { success: false, error: error as Error };
    }
}

const result = safeDivide(10, 2);
if (result.success) {
    console.log("Result:", result.data);
} else {
    console.log("Error:", result.error.message);
}
Beginner
15. What are decorators in TypeScript?

Decorators are special declarations that can be attached to classes, methods, accessors, properties, or parameters. They are used to modify behavior.

  • Class decorator: @sealed
  • Method decorator: @log
  • Property decorator: @defaultValue
  • Parameter decorator: @inject
  • Use cases: logging, validation, dependency injection
typescript
// Arrow Functions and Closures in TypeScript
// Basic arrow function
const square = (x: number): number => x * x;

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

// Arrow function with block body
const multiply = (a: number, b: number): number => {
    const result = a * b;
    return result;
};

// Higher-order arrow function
const operate = (a: number, b: number, operation: (x: number, y: number) => number): number => {
    return operation(a, b);
};

// Arrow function with closure
const makeMultiplier = (factor: number): (x: number) => number => {
    return (x: number): number => x * factor;
};

// Arrow function with this binding
class Counter {
    count: number = 0;
    
    // Arrow function preserves this
    increment = (): void => {
        this.count++;
    };
    
    // Arrow function in callback
    startTimer = (): void => {
        setInterval(() => {
            this.count++;
            console.log(this.count);
        }, 1000);
    };
}

// Usage
console.log(square(5));
console.log(add(5, 3));
console.log(multiply(5, 3));
console.log(operate(6, 7, (a, b) => a * b));

const double = makeMultiplier(2);
console.log(double(5));

// Closure example
function createCounter(): { increment: () => number; getCount: () => number } {
    let count = 0;
    return {
        increment: () => ++count,
        getCount: () => count
    };
}

const counter = createCounter();
console.log(counter.increment());
console.log(counter.increment());
console.log(counter.getCount());

// Arrow functions with arrays
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
const evens = numbers.filter(x => x % 2 === 0);
const sum = numbers.reduce((acc, x) => acc + x, 0);

console.log(doubled);
console.log(evens);
console.log(sum);
Intermediate
16. What are utility types in TypeScript?

Utility types are built-in generic types that transform existing types. They are provided by the TypeScript standard library.

  • Partial: Partial<T> – makes all properties optional
  • Required: Required<T> – makes all properties required
  • Readonly: Readonly<T> – makes all properties readonly
  • Pick: Pick<T, K> – selects a subset of properties
  • Omit: Omit<T, K> – omits a set of properties
  • Record: Record<K, T> – maps keys to a type
  • Exclude: Exclude<T, U> – removes types
  • Extract: Extract<T, U> – extracts types
  • NonNullable: NonNullable<T> – removes null/undefined
  • ReturnType: ReturnType<T> – gets the return type of a function
  • Parameters: Parameters<T> – gets tuple of function parameters
typescript
// Type Guards and Type Narrowing in TypeScript
// typeof type guard
function processValue(value: string | number): string {
    if (typeof value === "string") {
        return `String: ${value.toUpperCase()}`;
    }
    return `Number: ${value.toFixed(2)}`;
}

// instanceof type guard
class Animal { name: string = ""; }
class Dog extends Animal { breed: string = ""; }

function describeAnimal(animal: Animal): string {
    if (animal instanceof Dog) {
        return `Dog: ${animal.name}, ${animal.breed}`;
    }
    return `Animal: ${animal.name}`;
}

// Custom type guard
interface User {
    name: string;
    email: string;
}

interface Admin {
    name: string;
    role: string;
    permissions: string[];
}

function isAdmin(user: User | Admin): user is Admin {
    return (user as Admin).role !== undefined;
}

function processUser(user: User | Admin): string {
    if (isAdmin(user)) {
        return `Admin: ${user.name}, Role: ${user.role}`;
    }
    return `User: ${user.name}, Email: ${user.email}`;
}

// Discriminated union
interface Square {
    kind: "square";
    size: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Circle;

function area(shape: Shape): number {
    switch (shape.kind) {
        case "square":
            return shape.size * shape.size;
        case "circle":
            return Math.PI * shape.radius * shape.radius;
    }
}

// Assertion functions
function assertIsString(value: any): asserts value is string {
    if (typeof value !== "string") {
        throw new Error("Value is not a string");
    }
}

function useString(value: any): string {
    assertIsString(value);
    return value.toUpperCase();
}

// Usage
console.log(processValue("hello"));
console.log(processValue(42));

const dog = new Dog();
dog.name = "Rex";
dog.breed = "German Shepherd";
console.log(describeAnimal(dog));

const user: User = { name: "Alice", email: "alice@example.com" };
const admin: Admin = { name: "Bob", role: "admin", permissions: ["read"] };
console.log(processUser(user));
console.log(processUser(admin));

const square: Square = { kind: "square", size: 5 };
const circle: Circle = { kind: "circle", radius: 3 };
console.log(area(square));
console.log(area(circle));

console.log(useString("hello"));
Intermediate
17. What are mapped types in TypeScript?

Mapped types transform properties of an existing type by iterating over its keys. They are used to create new types from existing ones.

  • Basic mapped: type Readonly<T> = { readonly [P in keyof T]: T[P] }
  • Optional: type Partial<T> = { [P in keyof T]?: T[P] }
  • Mapping modifiers: + and - (e.g., readonly, ?)
  • Key remapping: [P in keyof T as NewKey]: ...
  • Template literal types: [P in keyof T as `get${Capitalize<string & P>}`]: ...
typescript
// Utility Types in TypeScript
// Partial
interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number; }

// Required
type RequiredUser = Required<PartialUser>;
// { id: number; name: string; email: string; age: number; }

// Readonly
type ReadonlyUser = Readonly<User>;
// { readonly id: number; readonly name: string; readonly email: string; readonly age: number; }

// Pick
type UserName = Pick<User, "name" | "email">;
// { name: string; email: string; }

// Omit
type UserWithoutId = Omit<User, "id">;
// { name: string; email: string; age: number; }

// Exclude
type Status = "active" | "inactive" | "pending";
type ActiveStatus = Exclude<Status, "inactive" | "pending">;
// "active"

// Extract
type ValidStatus = Extract<Status, "active" | "pending">;
// "active" | "pending"

// NonNullable
type Nullable = string | null | undefined;
type NonNullableString = NonNullable<Nullable>;
// string

// ReturnType
function getUser(): User {
    return { id: 1, name: "Alice", email: "alice@example.com", age: 25 };
}
type UserReturnType = ReturnType<typeof getUser>;
// User

// Parameters
function greet(name: string, title: string): string {
    return `Hello, ${title} ${name}`;
}
type GreetParams = Parameters<typeof greet>;
// [string, string]

// Record
type UserMap = Record<string, User>;
// { [key: string]: User }

// Usage
const partialUser: PartialUser = { name: "Alice" };
const readonlyUser: ReadonlyUser = { id: 1, name: "Alice", email: "alice@example.com", age: 25 };
// readonlyUser.id = 2; // Error: Cannot assign to 'id' because it is a read-only property

const userMap: UserMap = {
    "user1": { id: 1, name: "Alice", email: "alice@example.com", age: 25 },
    "user2": { id: 2, name: "Bob", email: "bob@example.com", age: 30 }
};
Intermediate
18. What are conditional types in TypeScript?

Conditional types select a type based on a condition, like a ternary operator on types. They enable advanced type logic.

  • Syntax: T extends U ? X : Y
  • Infer: type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
  • Distributive: conditional types distribute over union types
  • Use cases: extracting types, filtering, recursive types
  • Recursive: type Flatten<T> = T extends any[] ? Flatten<T[number]> : T
typescript
// Generics in TypeScript
// Generic function
function identity<T>(value: T): T {
    return value;
}

// Generic with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

// Generic interface
interface Box<T> {
    value: T;
    getValue(): T;
}

// Generic class
class Stack<T> {
    private items: T[] = [];
    
    push(item: T): void {
        this.items.push(item);
    }
    
    pop(): T | undefined {
        return this.items.pop();
    }
    
    peek(): T | undefined {
        return this.items[this.items.length - 1];
    }
    
    isEmpty(): boolean {
        return this.items.length === 0;
    }
}

// Generic with multiple types
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

// Generic with default type
function createArray<T = string>(length: number, value: T): T[] {
    return Array(length).fill(value);
}

// Generic constraints with keyof
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
    return items.map(item => item[key]);
}

// Usage
console.log(identity<string>("Hello"));
console.log(identity<number>(42));

const user = { id: 1, name: "Alice", age: 25 };
console.log(getProperty(user, "name"));

const stringBox: Box<string> = {
    value: "Hello",
    getValue() { return this.value; }
};
console.log(stringBox.getValue());

const stack = new Stack<number>();
stack.push(1);
stack.push(2);
console.log(stack.pop());

const merged = merge({ name: "Alice" }, { age: 25 });
console.log(merged);

const stringArray = createArray(3, "Hello");
const numberArray = createArray<number>(3, 42);

const users = [
    { id: 1, name: "Alice", age: 25 },
    { id: 2, name: "Bob", age: 30 }
];
const names = pluck(users, "name");
console.log(names);
Intermediate
19. What are keyof and typeof operators in TypeScript?

keyof gets the union of keys of a type. typeof gets the type of a value, often used with keyof and typeof together.

  • keyof: type UserKeys = keyof User
  • typeof: const obj = { name: "Alice" }; type Obj = typeof obj;
  • keyof typeof: get keys of an object
  • Lookup types: User["name"]
  • Generics with keyof: function getProp<T, K extends keyof T>(obj: T, key: K)
typescript
// Decorators in TypeScript
// Class decorator
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

// Method decorator
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args: any[]) {
        console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
        const result = originalMethod.apply(this, args);
        console.log(`${propertyKey} returned: ${JSON.stringify(result)}`);
        return result;
    };
}

// Property decorator
function format(target: any, propertyKey: string) {
    let value: string;
    
    const getter = function() {
        return value;
    };
    
    const setter = function(newVal: string) {
        value = newVal.toUpperCase();
    };
    
    Object.defineProperty(target, propertyKey, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
    });
}

// Accessor decorator
function configurable(value: boolean) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
    };
}

// Parameter decorator
function required(target: any, propertyKey: string, parameterIndex: number) {
    // Implementation
    console.log(`Parameter ${parameterIndex} of ${propertyKey} is required`);
}

// Using decorators
@sealed
class User {
    @format
    name: string;
    
    @log
    greet(@required message: string): string {
        return `${this.name} says: ${message}`;
    }
    
    @configurable(false)
    get fullName(): string {
        return `User: ${this.name}`;
    }
}

// Usage
const user = new User();
user.name = "alice";
console.log(user.name); // ALICE
console.log(user.greet("Hello"));

// Decorator factory
function validate(minLength: number) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function(...args: any[]) {
            if (args[0] && args[0].length < minLength) {
                throw new Error(`${propertyKey} must be at least ${minLength} characters`);
            }
            return originalMethod.apply(this, args);
        };
    };
}

class Validator {
    @validate(5)
    setName(name: string): void {
        console.log(`Name set to: ${name}`);
    }
}

const validator = new Validator();
validator.setName("Alice");
// validator.setName("Al"); // Throws error
Intermediate
20. What is declaration merging in TypeScript?

Declaration merging is the ability to combine multiple declarations of the same name into a single definition. This works for interfaces, namespaces, and enums.

  • Interface merging: multiple interfaces with the same name are merged
  • Namespace merging: namespaces can be extended
  • Enum merging: enums can be merged
  • Augmenting modules: add new declarations to existing modules
  • Use case: extending third-party types
typescript
// Modules in TypeScript
// Exporting
export interface User {
    id: number;
    name: string;
    email: string;
}

export class UserService {
    private users: User[] = [];
    
    addUser(user: User): void {
        this.users.push(user);
    }
    
    getUsers(): User[] {
        return this.users;
    }
}

export const API_URL = "https://api.example.com";

export default function createUser(name: string, email: string): User {
    return {
        id: Date.now(),
        name,
        email
    };
}

// Importing (in another file)
/*
import createUser, { User, UserService, API_URL } from './user';

const user = createUser("Alice", "alice@example.com");
const service = new UserService();
service.addUser(user);
console.log(API_URL);
*/

// Namespace (internal modules)
namespace MathUtils {
    export function add(a: number, b: number): number {
        return a + b;
    }
    
    export function subtract(a: number, b: number): number {
        return a - b;
    }
    
    export namespace Advanced {
        export function multiply(a: number, b: number): number {
            return a * b;
        }
    }
}

// Using namespace
console.log(MathUtils.add(5, 3));
console.log(MathUtils.Advanced.multiply(5, 3));

// Ambient modules (declaration files)
// Example: typings.d.ts
/*
declare module "my-library" {
    export function doSomething(): void;
    export const version: string;
}
*/

// Module augmentation
declare module './user' {
    interface User {
        age?: number;
    }
}
Intermediate
21. What is tsconfig.json and its important options?

tsconfig.json is the configuration file for TypeScript projects. It controls compiler options, file inclusion, and project settings.

  • compilerOptions: target, module, strict, outDir, rootDir, etc.
  • Strict flags: strict, noImplicitAny, strictNullChecks
  • include/exclude: which files to compile
  • references: project references for monorepos
  • Paths: path mapping for module resolution
typescript
// Async/Await in TypeScript
// Basic async function
async function fetchData(): Promise<string> {
    await new Promise(resolve => setTimeout(resolve, 1000));
    return "Data loaded";
}

// Async with error handling
async function fetchWithError(): Promise<string> {
    try {
        const data = await fetchData();
        return `Success: ${data}`;
    } catch (error) {
        return `Error: ${error}`;
    }
}

// Async with multiple promises
async function fetchMultiple(): Promise<string[]> {
    const [result1, result2] = await Promise.all([
        fetchData(),
        fetchData()
    ]);
    return [result1, result2];
}

// Async with timeout
async function fetchWithTimeout(timeout: number): Promise<string> {
    const timeoutPromise = new Promise<never>((_, reject) => {
        setTimeout(() => reject(new Error("Timeout")), timeout);
    });
    
    const dataPromise = fetchData();
    
    return await Promise.race([dataPromise, timeoutPromise]);
}

// Async with retry
async function fetchWithRetry(retries: number): Promise<string> {
    for (let i = 0; i < retries; i++) {
        try {
            return await fetchData();
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
    }
    throw new Error("All retries failed");
}

// Usage
async function main() {
    console.log(await fetchData());
    console.log(await fetchWithError());
    console.log(await fetchMultiple());
    
    try {
        console.log(await fetchWithTimeout(500));
    } catch (error) {
        console.log("Timeout!");
    }
    
    try {
        console.log(await fetchWithRetry(3));
    } catch (error) {
        console.log("All retries failed");
    }
}

main();
Intermediate
22. What is strict mode in TypeScript?

Strict mode enables a set of type-checking rules that catch potential errors. It is recommended for all projects.

  • strict: enables all strict family options
  • noImplicitAny: raises errors on implicit any
  • strictNullChecks: distinguishes null/undefined from other types
  • strictFunctionTypes: stricter function parameter checking
  • strictPropertyInitialization: ensures class properties are initialized
typescript
// Iterators and Generators in TypeScript
// Generator function
function* numberGenerator(): Generator<number> {
    yield 1;
    yield 2;
    yield 3;
}

// Generator with infinite sequence
function* infiniteGenerator(): Generator<number> {
    let i = 0;
    while (true) {
        yield i++;
    }
}

// Generator with return value
function* generatorWithReturn(): Generator<number, string, void> {
    yield 1;
    yield 2;
    yield 3;
    return "Done";
}

// Generator with input
function* generatorWithInput(): Generator<number, void, number> {
    const input = yield 1;
    const result = yield input * 2;
    return result;
}

// Async generator
async function* asyncGenerator(): AsyncGenerator<number> {
    for (let i = 1; i <= 5; i++) {
        await new Promise(resolve => setTimeout(resolve, 100));
        yield i;
    }
}

// Custom iterable
class Range implements Iterable<number> {
    constructor(private start: number, private end: number) {}
    
    *[Symbol.iterator](): Iterator<number> {
        for (let i = this.start; i <= this.end; i++) {
            yield i;
        }
    }
}

// Usage
const gen = numberGenerator();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

const infinite = infiniteGenerator();
console.log(infinite.next().value);
console.log(infinite.next().value);

const withReturn = generatorWithReturn();
console.log(withReturn.next());
console.log(withReturn.next());
console.log(withReturn.next());
console.log(withReturn.next());

const withInput = generatorWithInput();
const first = withInput.next();
const second = withInput.next(5);
console.log(second);

// Async generator
async function processAsyncGenerator() {
    for await (const value of asyncGenerator()) {
        console.log(value);
    }
}
processAsyncGenerator();

// Range iterable
const range = new Range(1, 5);
for (const num of range) {
    console.log(num);
}
Intermediate
23. How does type inference work in TypeScript?

Type inference is the ability of the compiler to automatically deduce types based on context, reducing the need for explicit annotations.

  • Variable inference: let x = 5 infers number
  • Function return inference: return type is inferred from the body
  • Contextual typing: type of function parameter inferred from usage
  • Best common type: when inferring from multiple expressions
  • Non-inferable: explicit annotations may be needed
typescript
// Observables and Subjects in TypeScript
// Simple Observer implementation
interface Observer<T> {
    next(value: T): void;
    error(error: any): void;
    complete(): void;
}

class Observable<T> {
    private observers: Observer<T>[] = [];
    
    subscribe(observer: Observer<T>): () => void {
        this.observers.push(observer);
        return () => {
            const index = this.observers.indexOf(observer);
            if (index !== -1) {
                this.observers.splice(index, 1);
            }
        };
    }
    
    next(value: T): void {
        for (const observer of this.observers) {
            observer.next(value);
        }
    }
    
    error(error: any): void {
        for (const observer of this.observers) {
            observer.error(error);
        }
    }
    
    complete(): void {
        for (const observer of this.observers) {
            observer.complete();
        }
    }
}

// Subject (hot observable)
class Subject<T> extends Observable<T> {
    private value: T | undefined;
    
    next(value: T): void {
        this.value = value;
        super.next(value);
    }
    
    getValue(): T | undefined {
        return this.value;
    }
}

// BehaviorSubject (Subject with initial value)
class BehaviorSubject<T> extends Subject<T> {
    constructor(initialValue: T) {
        super();
        this.value = initialValue;
    }
    
    getValue(): T {
        return this.value as T;
    }
}

// Usage
const observable = new Observable<number>();
const unsubscribe = observable.subscribe({
    next: (value) => console.log(`Observer 1: ${value}`),
    error: (error) => console.log(`Error: ${error}`),
    complete: () => console.log("Completed")
});

observable.next(1);
observable.next(2);
unsubscribe();
observable.next(3); // Will not be received

const subject = new Subject<string>();
subject.subscribe({
    next: (value) => console.log(`Subject Observer: ${value}`)
});
subject.next("Hello");
console.log(subject.getValue());

const behaviorSubject = new BehaviorSubject<number>(0);
behaviorSubject.subscribe({
    next: (value) => console.log(`BehaviorSubject: ${value}`)
});
behaviorSubject.next(42);
console.log(behaviorSubject.getValue());
Intermediate
24. What are declaration files (.d.ts) in TypeScript?

Declaration files provide type information for existing JavaScript libraries. They are used to add TypeScript support to non-TypeScript code.

  • Declaration file: *.d.ts
  • DefinitelyTyped: @types packages
  • Ambient declarations: describe global libraries
  • Module declarations: describe module shapes
  • Triple-slash references: /// <reference types="..." />
typescript
// Promises in TypeScript
// Basic promise
const promise: Promise<string> = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Success!");
    }, 1000);
});

// Promise with error
const promiseWithError: Promise<string> = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error("Failed!"));
    }, 1000);
});

// Promise chaining
function fetchUser(): Promise<{ id: number; name: string }> {
    return Promise.resolve({ id: 1, name: "Alice" });
}

function fetchPosts(userId: number): Promise<string[]> {
    return Promise.resolve(["Post 1", "Post 2"]);
}

fetchUser()
    .then(user => {
        console.log(`User: ${user.name}`);
        return fetchPosts(user.id);
    })
    .then(posts => {
        console.log(`Posts: ${posts.join(", ")}`);
    })
    .catch(error => {
        console.error(`Error: ${error}`);
    });

// Promise.all
const promises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3)
];

Promise.all(promises)
    .then(results => {
        console.log("All results:", results);
    })
    .catch(error => {
        console.error("Error:", error);
    });

// Promise.race
const racePromises = [
    new Promise(resolve => setTimeout(resolve, 1000, "First")),
    new Promise(resolve => setTimeout(resolve, 500, "Second")),
    new Promise(resolve => setTimeout(resolve, 2000, "Third"))
];

Promise.race(racePromises)
    .then(result => {
        console.log("Race winner:", result);
    });

// Promise.allSettled (ES2020)
Promise.allSettled(promises)
    .then(results => {
        for (const result of results) {
            if (result.status === "fulfilled") {
                console.log("Fulfilled:", result.value);
            } else {
                console.log("Rejected:", result.reason);
            }
        }
    });

// Promise.any (ES2021)
Promise.any(racePromises)
    .then(result => {
        console.log("Any result:", result);
    })
    .catch(error => {
        console.log("All rejected:", error);
    });

// Custom promise type with type safety
type Deferred<T> = {
    promise: Promise<T>;
    resolve: (value: T) => void;
    reject: (reason?: any) => void;
};

function createDeferred<T>(): Deferred<T> {
    let resolve!: (value: T) => void;
    let reject!: (reason?: any) => void;
    const promise = new Promise<T>((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

const deferred = createDeferred<string>();
deferred.promise.then(value => console.log("Deferred:", value));
deferred.resolve("Deferred resolved!");
Intermediate
25. What are namespaces in TypeScript?

Namespaces group related code under a common name, preventing global scope pollution. They are the older module system, now less common.

  • Namespace: namespace MyLib { export function add() { } }
  • Nested: namespace Outer { export namespace Inner { } }
  • Alias: import Add = MyLib.add
  • Ambient: declare namespace MyLib
  • Use case: older code, global libraries
typescript
// Type Inference and Type Annotations in TypeScript
// Basic type inference
let inferredString = "Hello"; // string
let inferredNumber = 42; // number
let inferredBoolean = true; // boolean
let inferredArray = [1, 2, 3]; // number[]

// Type annotations
let explicitString: string = "Hello";
let explicitNumber: number = 42;
let explicitBoolean: boolean = true;
let explicitArray: number[] = [1, 2, 3];

// Contextual typing
window.onmousedown = function(mouseEvent) {
    // mouseEvent is inferred as MouseEvent
    console.log(mouseEvent.button);
};

// Type assertion
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
let strLength2: number = (<string>someValue).length;

// Type inference in functions
function add(x: number, y: number) {
    return x + y; // Return type inferred as number
}

// Best common type
let mixedArray = [1, "hello", true]; // (string | number | boolean)[]

// Contextual typing with generics
function identity<T>(value: T): T {
    return value;
}
let result = identity("Hello"); // result is inferred as string

// Type inference with object literals
let person = {
    name: "Alice",
    age: 25
};
// person is inferred as { name: string; age: number }

// Type inference with class
class Person {
    constructor(public name: string, public age: number) {}
}
let alice = new Person("Alice", 25);
// alice is inferred as Person

// Type inference with union types
let value: string | number = "Hello";
value = 42;

// Type inference with conditional types
type IsString<T> = T extends string ? true : false;
type Result = IsString<"hello">; // true

// Usage
console.log(inferredString);
console.log(explicitString);
console.log(strLength);
console.log(add(5, 3));
Intermediate
26. What is the satisfies operator in TypeScript?

The satisfies operator (satisfies) ensures that an expression matches a type without affecting its inferred type. It is useful for validation while preserving inference.

  • Syntax: const obj = { name: "Alice" } satisfies HasName
  • Preserves inference: does not widen the type
  • Use case: ensure object matches interface without losing literal types
  • Introduced: TypeScript 4.9
  • Comparison: as forces type, satisfies checks without forcing
typescript
// Conditional Types in TypeScript
// Basic conditional type
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false

// Conditional type with infer
type ElementType<T> = T extends (infer U)[] ? U : T;
type C = ElementType<string[]>; // string
type D = ElementType<number>; // number

// Conditional type with union distribution
type ToArray<T> = T extends any ? T[] : never;
type E = ToArray<string | number>; // string[] | number[]

// Type guard with conditional types
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;
type F = IsFunction<(x: number) => string>; // true
type G = IsFunction<string>; // false

// Conditional type with keyof
type GetProperty<T, K> = K extends keyof T ? T[K] : never;
interface User {
    name: string;
    age: number;
}
type H = GetProperty<User, "name">; // string
type I = GetProperty<User, "email">; // never

// Conditional type with recursion
type Flatten<T> = T extends any[] ? T[number] : T;
type J = Flatten<string[]>; // string
type K = Flatten<number>; // number

// Conditional type with never
type NonNullable<T> = T extends null | undefined ? never : T;
type L = NonNullable<string | null>; // string

// Conditional type with tuple
type FirstElement<T> = T extends [infer F, ...any[]] ? F : never;
type M = FirstElement<[1, 2, 3]>; // 1
type N = FirstElement<[]>; // never

// Usage
type IsStringResult = IsString<"hello">;
type ElementTypeResult = ElementType<number[]>;
Intermediate
27. What are branded types in TypeScript?

Branded types (nominal types) use a unique tag to distinguish types with the same underlying structure, providing type safety for different domains.

  • Branding: type UserId = string & { __brand: "UserId" }
  • Factory: function createUserId(id: string): UserId { return id as UserId; }
  • Check: ensure values are created through factories
  • Use case: preventing mixing of domain values (e.g., UserId vs ProductId)
  • Alternative: class with private field
typescript
// Mapped Types in TypeScript
// Basic mapped type
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

type Partial<T> = {
    [P in keyof T]?: T[P];
};

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

// Mapped type with transformation
type Nullable<T> = {
    [P in keyof T]: T[P] | null;
};

type Stringify<T> = {
    [P in keyof T]: string;
};

// Mapping over union
type Status = "active" | "inactive" | "pending";
type StatusMap = {
    [K in Status]: string;
};
// { active: string; inactive: string; pending: string; }

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

// Filtering keys
type FilterKeys<T, U> = {
    [P in keyof T]: T[P] extends U ? P : never;
}[keyof T];

// Usage
interface User {
    id: number;
    name: string;
    age: number;
}

type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
type NullableUser = Nullable<User>;

type UserName = Pick<User, "name">;

const statusMap: StatusMap = {
    active: "Active",
    inactive: "Inactive",
    pending: "Pending"
};

type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; getId: () => number; }
Intermediate
28. How to type the 'this' keyword in TypeScript?

TypeScript allows specifying the type of this in functions and methods, improving safety when using callbacks or manipulating context.

  • this parameter: function fn(this: SomeType, param: string)
  • Method annotation: method(this: ThisType, arg: any)
  • Arrow functions: capture lexical this
  • Call/apply/bind: fn.call(context, arg)
  • ThisType: utility for context type
typescript
// Template Literal Types in TypeScript
// Basic template literal type
type Greeting = `Hello, ${string}`;
type H = Greeting; // "Hello, " + string

// Template literal with union
type Color = "red" | "green" | "blue";
type ColorMessage = `Color: ${Color}`;
// "Color: red" | "Color: green" | "Color: blue"

// Template literal with mapped types
type EventName = `on${Capitalize<"click" | "hover" | "focus">}`;
// "onClick" | "onHover" | "onFocus"

// Template literal with conditional types
type Path<T extends string> = T extends `/${infer U}` ? U : T;
type P = Path<"/users">; // "users"

// Template literal with object keys
type ObjectKeys<T> = {
    [K in keyof T]: `get${Capitalize<string & K>}`;
}[keyof T];

// Template literal with string manipulation
type UppercaseKeys<T> = {
    [K in keyof T as Uppercase<string & K>]: T[K];
};

// Template literal for API endpoints
type ApiMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiPath = `/api/${string}`;
type ApiEndpoint = `${Lowercase<ApiMethod>} ${ApiPath}`;
// "get /api/..." | "post /api/..." | ...

// Template literal for CSS
type CSSUnit = `${number}${"px" | "em" | "rem" | "%"}`;
type CSSProperty = `${string}:${CSSUnit}`;

// Usage
const greeting: Greeting = "Hello, World";
const colorMessage: ColorMessage = "Color: red";
const eventName: EventName = "onClick";

interface User {
    id: number;
    name: string;
    age: number;
}
type UserKeys = ObjectKeys<User>; // "getId" | "getName" | "getAge"

type UppercaseUser = UppercaseKeys<User>;
// { ID: number; NAME: string; AGE: number; }

const apiEndpoint: ApiEndpoint = "get /api/users";
const cssProperty: CSSProperty = "color:red";
Advanced
29. What are template literal types in TypeScript?

Template literal types create new string literal types by concatenating strings, using union types and type inference.

  • Syntax: `Hello, ${string}`
  • Union expansion: type Status = `${"success" | "error"}`
  • Infer: type ExtractName<T> = T extends `Hello, ${infer Name}` ? Name : never
  • Recursive: can be used recursively
  • Use case: constructing CSS class names, event names, etc.
typescript
// Type Guards and Assertion Functions
// Type guard with typeof
function isString(value: unknown): value is string {
    return typeof value === "string";
}

// Type guard with instanceof
function isDate(value: unknown): value is Date {
    return value instanceof Date;
}

// Type guard with custom predicate
interface User {
    name: string;
    email: string;
}

function isUser(value: any): value is User {
    return value && typeof value.name === "string" && typeof value.email === "string";
}

// Type guard for array
function isArray<T>(value: any): value is T[] {
    return Array.isArray(value);
}

// Type guard for union
type Animal = Dog | Cat;
interface Dog {
    type: "dog";
    breed: string;
}
interface Cat {
    type: "cat";
    color: string;
}

function isDog(animal: Animal): animal is Dog {
    return animal.type === "dog";
}

// Assertion function
function assertIsString(value: any): asserts value is string {
    if (typeof value !== "string") {
        throw new Error("Value is not a string");
    }
}

function assertIsNumber(value: any): asserts value is number {
    if (typeof value !== "number") {
        throw new Error("Value is not a number");
    }
}

function assertIsUser(value: any): asserts value is User {
    if (!value || typeof value.name !== "string" || typeof value.email !== "string") {
        throw new Error("Value is not a User");
    }
}

// Usage
const value: unknown = "Hello";
if (isString(value)) {
    console.log(value.toUpperCase());
}

const date: unknown = new Date();
if (isDate(date)) {
    console.log(date.getFullYear());
}

const user: any = { name: "Alice", email: "alice@example.com" };
if (isUser(user)) {
    console.log(user.name);
}

const animal: Animal = { type: "dog", breed: "German Shepherd" };
if (isDog(animal)) {
    console.log(animal.breed);
}

function processValue(value: any) {
    assertIsString(value);
    console.log(value.toUpperCase());
}

function processUser(value: any) {
    assertIsUser(value);
    console.log(`${value.name} (${value.email})`);
}

processValue("Hello");
processUser({ name: "Alice", email: "alice@example.com" });
Advanced
30. What are some advanced type manipulation techniques in TypeScript?

Advanced type manipulation includes recursive types, type-safe APIs, and metaprogramming using conditional, mapped, and template literal types.

  • Recursive types: type JSONValue = string | number | boolean | null | JSONObject | JSONArray
  • Type-safe query builders: using mapped types
  • Transformations: type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] }
  • Pattern matching: using conditional types with infer
  • Variadic tuple types: spread tuples
typescript
// Reflection and Metadata in TypeScript
// Using decorators for metadata
import 'reflect-metadata';

// Metadata keys
const METADATA_KEYS = {
    designType: "design:type",
    designParamTypes: "design:paramtypes",
    designReturnType: "design:returntype",
    route: "route",
    method: "method"
};

// Route decorator
function Route(path: string) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        Reflect.defineMetadata(METADATA_KEYS.route, path, target, propertyKey);
    };
}

// Method decorator
function Method(method: string) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        Reflect.defineMetadata(METADATA_KEYS.method, method, target, propertyKey);
    };
}

// Class decorator for metadata
function Controller(basePath: string) {
    return function(target: Function) {
        Reflect.defineMetadata("basePath", basePath, target);
    };
}

// Using decorators
@Controller("/api")
class UserController {
    @Route("/users")
    @Method("GET")
    getUsers(): User[] {
        return [
            { id: 1, name: "Alice", email: "alice@example.com" },
            { id: 2, name: "Bob", email: "bob@example.com" }
        ];
    }
    
    @Route("/users/:id")
    @Method("GET")
    getUser(id: number): User {
        return { id, name: "Alice", email: "alice@example.com" };
    }
    
    @Route("/users")
    @Method("POST")
    createUser(user: User): User {
        return user;
    }
}

// Metadata reflection
function getMethodMetadata(target: any, propertyKey: string) {
    const route = Reflect.getMetadata(METADATA_KEYS.route, target, propertyKey);
    const method = Reflect.getMetadata(METADATA_KEYS.method, target, propertyKey);
    const paramTypes = Reflect.getMetadata(METADATA_KEYS.designParamTypes, target, propertyKey);
    const returnType = Reflect.getMetadata(METADATA_KEYS.designReturnType, target, propertyKey);
    return { route, method, paramTypes, returnType };
}

// Usage
const controller = new UserController();
const metadata = getMethodMetadata(UserController.prototype, "getUsers");
console.log(metadata);

// Getting class metadata
const basePath = Reflect.getMetadata("basePath", UserController);
console.log(`Base path: ${basePath}`);

// Custom metadata
interface RouteMetadata {
    path: string;
    method: string;
    handler: Function;
}

function getRoutes<T>(target: new (...args: any[]) => T): RouteMetadata[] {
    const prototype = target.prototype;
    const routes: RouteMetadata[] = [];
    const propertyNames = Object.getOwnPropertyNames(prototype);
    
    for (const propertyName of propertyNames) {
        if (propertyName === "constructor") continue;
        const route = Reflect.getMetadata(METADATA_KEYS.route, prototype, propertyName);
        const method = Reflect.getMetadata(METADATA_KEYS.method, prototype, propertyName);
        if (route && method) {
            routes.push({
                path: route,
                method: method,
                handler: prototype[propertyName]
            });
        }
    }
    return routes;
}

const routes = getRoutes(UserController);
console.log(routes);
Coding Round
31. Reverse a string

Reverse a string using JavaScript methods or manual iteration with TypeScript types.

  • Built-in: str.split('').reverse().join('')
  • Spread operator: [...str].reverse().join('')
  • Manual: Iterate from end to start
  • Return type: string
typescript
// Reverse a string in TypeScript
function reverseString(str: string): string {
    return str.split('').reverse().join('');
}

console.log(reverseString("hello"));  // "olleh"

// Using spread operator
function reverseStringSpread(str: string): string {
    return [...str].reverse().join('');
}

console.log(reverseStringSpread("hello"));  // "olleh"

// Manual implementation
function reverseStringManual(str: string): string {
    let result = "";
    for (let i = str.length - 1; i >= 0; i--) {
        result += str[i];
    }
    return result;
}

console.log(reverseStringManual("hello"));  // "olleh"
Coding Round
32. Check palindrome

Check if a string is a palindrome using JavaScript methods or two-pointer approach with TypeScript types.

  • Built-in: str === str.split('').reverse().join('')
  • Two-pointer: Compare from both ends
  • Case insensitive: toLowerCase()
  • Return type: boolean
typescript
// Check palindrome in TypeScript
function isPalindrome(str: string): boolean {
    const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
    return cleaned === cleaned.split('').reverse().join('');
}

console.log(isPalindrome("racecar"));  // true
console.log(isPalindrome("hello"));   // false

// Two-pointer approach
function isPalindromeTwoPointer(str: string): boolean {
    const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
    let left = 0;
    let right = cleaned.length - 1;
    while (left < right) {
        if (cleaned[left] !== cleaned[right]) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

console.log(isPalindromeTwoPointer("A man a plan a canal Panama"));  // true
Coding Round
33. Find max in array

Find maximum value using Math.max or manual iteration with TypeScript.

  • Built-in: Math.max(...arr)
  • Manual: Iterate and track max
  • Empty array: Return undefined
  • Return type: number | undefined
typescript
// Find max in array in TypeScript
function findMax(arr: number[]): number | undefined {
    return arr.length > 0 ? Math.max(...arr) : undefined;
}

console.log(findMax([1, 5, 3, 9, 2]));  // 9

// Manual implementation
function findMaxManual(arr: number[]): number | undefined {
    if (arr.length === 0) return undefined;
    let maxVal = arr[0];
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > maxVal) {
            maxVal = arr[i];
        }
    }
    return maxVal;
}

console.log(findMaxManual([1, 5, 3, 9, 2]));  // 9

// Using reduce
function findMaxReduce(arr: number[]): number {
    return arr.reduce((a, b) => Math.max(a, b));
}
Coding Round
34. Remove duplicates

Remove duplicates using Set or filter method with TypeScript generics.

  • Set: [...new Set(arr)]
  • Filter: arr.filter((item, index) => arr.indexOf(item) === index)
  • Generic: <T>(arr: T[]): T[]
  • Complexity: O(n) time
typescript
// TypeScript - Remove Duplicates

// Method 1: Using Set (most efficient)
function removeDuplicatesSet<T>(arr: T[]): T[] {
    return [...new Set(arr)];
}

// Method 2: Using filter with indexOf
function removeDuplicatesFilter<T>(arr: T[]): T[] {
    return arr.filter((item, index) => arr.indexOf(item) === index);
}

// Method 3: Using reduce with object
function removeDuplicatesReduce<T extends string | number>(arr: T[]): T[] {
    const seen: Record<string, boolean> = {};
    return arr.reduce((acc, item) => {
        const key = String(item);
        if (!seen[key]) {
            seen[key] = true;
            acc.push(item);
        }
        return acc;
    }, [] as T[]);
}

// Method 4: Using Map for complex objects
function removeDuplicatesObjects<T extends object>(arr: T[], key: keyof T): T[] {
    const seen = new Map<any, boolean>();
    return arr.filter(item => {
        const value = item[key];
        if (!seen.has(value)) {
            seen.set(value, true);
            return true;
        }
        return false;
    });
}

// Method 5: Using for loop
function removeDuplicatesLoop<T>(arr: T[]): T[] {
    const result: T[] = [];
    for (let i = 0; i < arr.length; i++) {
        if (!result.includes(arr[i])) {
            result.push(arr[i]);
        }
    }
    return result;
}

// Example usage
const numbers = [1, 2, 2, 3, 4, 4, 5];
const strings = ["a", "b", "a", "c", "b", "d"];
const objects = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" },
    { id: 1, name: "Alice" },
    { id: 3, name: "Charlie" }
];

console.log("Numbers (Set):", removeDuplicatesSet(numbers));
console.log("Strings (Filter):", removeDuplicatesFilter(strings));
console.log("Numbers (Reduce):", removeDuplicatesReduce(numbers));
console.log("Objects (Map):", removeDuplicatesObjects(objects, "id"));
console.log("Numbers (Loop):", removeDuplicatesLoop(numbers));

// Generic type with constraints
function removeDuplicatesWithKey<T, K extends keyof T>(arr: T[], key: K): T[] {
    const seen = new Set<any>();
    return arr.filter(item => {
        const value = item[key];
        if (!seen.has(value)) {
            seen.add(value);
            return true;
        }
        return false;
    });
}

// Example with objects using a key
interface Person {
    id: number;
    name: string;
}

const people: Person[] = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" },
    { id: 1, name: "Alice" },
    { id: 3, name: "Charlie" }
];

console.log("People by id:", removeDuplicatesWithKey(people, "id"));

// Removing duplicates with custom equality function
function removeDuplicatesWithEquality<T>(
    arr: T[],
    areEqual: (a: T, b: T) => boolean
): T[] {
    const result: T[] = [];
    for (const item of arr) {
        if (!result.some(existing => areEqual(existing, item))) {
            result.push(item);
        }
    }
    return result;
}

// Example with custom equality
const points = [
    { x: 1, y: 2 },
    { x: 3, y: 4 },
    { x: 1, y: 2 },
    { x: 5, y: 6 }
];

const uniquePoints = removeDuplicatesWithEquality(
    points,
    (a, b) => a.x === b.x && a.y === b.y
);
console.log("Unique points:", uniquePoints);
Coding Round
35. Merge arrays

Merge arrays using spread operator or concat with TypeScript generics.

  • Spread: [...arr1, ...arr2]
  • concat: arr1.concat(arr2)
  • Generic: <T>(arr1: T[], arr2: T[]): T[]
  • Unique merge: [...new Set([...arr1, ...arr2])]
typescript
// Merge arrays in TypeScript
function mergeArrays<T>(arr1: T[], arr2: T[]): T[] {
    return [...arr1, ...arr2];
}

console.log(mergeArrays([1, 2], [3, 4]));  // [1, 2, 3, 4]

// Merge and remove duplicates
function mergeUnique<T>(arr1: T[], arr2: T[]): T[] {
    return [...new Set([...arr1, ...arr2])];
}

console.log(mergeUnique([1, 2, 3], [3, 4, 5]));  // [1, 2, 3, 4, 5]

// Type-safe merge
function mergeTyped<T extends object, U extends object>(arr1: T[], arr2: U[]): (T | U)[] {
    return [...arr1, ...arr2];
}
Coding Round
36. Convert string to number

Convert string to number using Number, parseInt, or parseFloat with TypeScript.

  • Number: Number(str)
  • parseInt: parseInt(str, 10)
  • parseFloat: parseFloat(str)
  • Return type: number | null
typescript
// Convert string to number in TypeScript
function stringToNumber(str: string): number | null {
    const num = Number(str);
    return isNaN(num) ? null : num;
}

console.log(stringToNumber("42"));  // 42
console.log(stringToNumber("invalid"));  // null

// With type safety
function stringToInt(str: string): number | null {
    const num = parseInt(str, 10);
    return isNaN(num) ? null : num;
}

function stringToFloat(str: string): number | null {
    const num = parseFloat(str);
    return isNaN(num) ? null : num;
}

console.log(stringToInt("42.5"));  // 42
console.log(stringToFloat("42.5"));  // 42.5

// With error handling
function safeStringToNumber(str: string): number {
    const num = Number(str);
    if (isNaN(num)) {
        throw new Error(`Invalid number: ${str}`);
    }
    return num;
}
Coding Round
37. Loop through dictionary

Iterate through object using for...in, Object.keys, or Object.entries with TypeScript.

  • for...in: for (const key in obj)
  • Object.keys: Object.keys(obj).forEach
  • Object.entries: Object.entries(obj).forEach
  • Type safety: Use interfaces for typed objects
typescript
// Loop through dictionary in TypeScript
interface Dictionary {
    [key: string]: any;
}

function loopDict(dict: Dictionary): void {
    for (const key in dict) {
        if (dict.hasOwnProperty(key)) {
            console.log(`${key} => ${dict[key]}`);
        }
    }
}

const data: Dictionary = { name: "Alice", age: 25, city: "NYC" };
loopDict(data);

// Using Object.keys
function loopDictKeys(dict: Dictionary): void {
    Object.keys(dict).forEach(key => {
        console.log(`${key} => ${dict[key]}`);
    });
}

// Using Object.entries
function loopDictEntries(dict: Dictionary): void {
    Object.entries(dict).forEach(([key, value]) => {
        console.log(`${key} => ${value}`);
    });
}

// Type-safe iteration
interface User {
    name: string;
    age: number;
    city: string;
}

function loopTypedDict(dict: User): void {
    const keys: (keyof User)[] = ["name", "age", "city"];
    for (const key of keys) {
        console.log(`${key} => ${dict[key]}`);
    }
}

loopTypedDict({ name: "Alice", age: 25, city: "NYC" });
Coding Round
38. Delay function execution

Delay execution using setTimeout, setInterval, or Promises with TypeScript.

  • setTimeout: setTimeout(fn, delay)
  • Promise: new Promise(resolve => setTimeout(resolve, delay))
  • async/await: await delay(1000)
  • Return type: Promise<void>
typescript
// Delay function execution in TypeScript
function delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// Usage with async/await
async function delayedExecution() {
    console.log("Start");
    await delay(2000);
    console.log("After 2 seconds");
}

delayedExecution();

// Using setTimeout with callback
function delayedCallback(ms: number, callback: () => void): void {
    setTimeout(callback, ms);
}

delayedCallback(2000, () => {
    console.log("After 2 seconds (callback)");
});

// Using setInterval
function intervalExecution(ms: number, callback: () => void): number {
    return setInterval(callback, ms);
}

const intervalId = intervalExecution(1000, () => {
    console.log("Repeating...");
});

// Clear interval
setTimeout(() => {
    clearInterval(intervalId);
    console.log("Interval stopped");
}, 5000);

// Promise-based delay with cancellation
function cancellableDelay(ms: number): { promise: Promise<void>; cancel: () => void } {
    let timeoutId: NodeJS.Timeout;
    let resolve: () => void;
    
    const promise = new Promise<void>((res) => {
        resolve = res;
        timeoutId = setTimeout(res, ms);
    });
    
    return {
        promise,
        cancel: () => {
            clearTimeout(timeoutId);
            resolve();
        }
    };
}

const { promise, cancel } = cancellableDelay(2000);
promise.then(() => console.log("Delayed execution completed"));
setTimeout(cancel, 1000); // Cancel after 1 second
Coding Round
39. HTTP GET request

Make HTTP GET requests using fetch with TypeScript types and interfaces.

  • fetch: fetch(url).then(res => res.json())
  • async/await: const response = await fetch(url)
  • Type safety: interface User { }
  • Generic: fetchData<T>(url: string): Promise<T>
typescript
// HTTP GET request in TypeScript
interface User {
    id: number;
    name: string;
    email: string;
}

// Using fetch with async/await
async function fetchData<T>(url: string): Promise<T> {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json() as T;
}

// With error handling
async function fetchUser(id: number): Promise<User | null> {
    try {
        const user = await fetchData<User>(`https://jsonplaceholder.typicode.com/users/${id}`);
        return user;
    } catch (error) {
        console.error("Error fetching user:", error);
        return null;
    }
}

// With timeout
async function fetchWithTimeout<T>(url: string, timeout: number): Promise<T> {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);
    
    try {
        const response = await fetch(url, { signal: controller.signal });
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return await response.json() as T;
    } finally {
        clearTimeout(timeoutId);
    }
}

// Usage
async function main() {
    const user = await fetchUser(1);
    console.log(user);
    
    try {
        const user2 = await fetchWithTimeout<User>('https://jsonplaceholder.typicode.com/users/1', 5000);
        console.log(user2);
    } catch (error) {
        console.log('Request timed out');
    }
}

main();

// Using axios alternative
// import axios from 'axios';
// async function fetchUserAxios(id: number): Promise<User> {
//     const response = await axios.get<User>(`https://jsonplaceholder.typicode.com/users/${id}`);
//     return response.data;
// }
Coding Round
40. Create a promise-like Deferred

Create a Deferred using Promises with TypeScript interfaces.

  • Deferred interface: interface Deferred<T> { promise: Promise<T>; resolve: (value: T) => void; reject: (reason?: any) => void; }
  • Generic: createDeferred<T>(): Deferred<T>
  • then method: Handle fulfillment and rejection
  • Chain: then().catch()
typescript
// Create a promise-like Deferred in TypeScript
interface Deferred<T> {
    promise: Promise<T>;
    resolve: (value: T | PromiseLike<T>) => void;
    reject: (reason?: any) => void;
}

function createDeferred<T>(): Deferred<T> {
    let resolve!: (value: T | PromiseLike<T>) => void;
    let reject!: (reason?: any) => void;
    const promise = new Promise<T>((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

// Usage
const deferred = createDeferred<string>();

deferred.promise
    .then(value => console.log(`Resolved: ${value}`))
    .catch(error => console.log(`Rejected: ${error}`));

setTimeout(() => {
    deferred.resolve("Success!");
}, 1000);

// With timeout
function createDeferredWithTimeout<T>(timeoutMs: number): Deferred<T> {
    const deferred = createDeferred<T>();
    const timeoutId = setTimeout(() => {
        deferred.reject(new Error("Deferred timeout"));
    }, timeoutMs);
    
    const originalResolve = deferred.resolve;
    deferred.resolve = (value: T | PromiseLike<T>) => {
        clearTimeout(timeoutId);
        originalResolve(value);
    };
    
    return deferred;
}

const deferredWithTimeout = createDeferredWithTimeout<string>(500);
deferredWithTimeout.promise
    .then(value => console.log(`Resolved: ${value}`))
    .catch(error => console.log(`Error: ${error.message}`));

// setTimeout(() => deferredWithTimeout.resolve("Success!"), 1000); // Will timeout
Coding Round
41. Factorial

Calculate factorial using recursion or iteration with TypeScript types.

  • Recursive: n * factorial(n-1)
  • Iterative: Loop with multiplication
  • Base case: n <= 1
  • Return type: number
typescript
// Factorial in TypeScript
function factorial(n: number): number {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

console.log(factorial(5));  // 120

// Iterative version
function factorialIterative(n: number): number {
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

console.log(factorialIterative(5));  // 120

// Using reduce
function factorialReduce(n: number): number {
    if (n <= 1) return 1;
    return Array.from({ length: n }, (_, i) => i + 1).reduce((a, b) => a * b, 1);
}

console.log(factorialReduce(5));  // 120

// Type-safe with bigint
function factorialBig(n: number): bigint {
    if (n <= 1) return 1n;
    return BigInt(n) * factorialBig(n - 1);
}

console.log(factorialBig(20).toString());
Coding Round
42. Fibonacci

Calculate Fibonacci using recursion, iteration, or memoization with TypeScript.

  • Recursive: fib(n-1) + fib(n-2)
  • Iterative: Loop with variables
  • Memoization: Cache in Map
  • Return type: number
typescript
// Fibonacci in TypeScript
function fibonacci(n: number): number {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(fibonacci(8));  // 21

// Iterative version
function fibonacciIterative(n: number): number {
    if (n <= 1) {
        return n;
    }
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
        const temp = a + b;
        a = b;
        b = temp;
    }
    return b;
}

console.log(fibonacciIterative(8));  // 21

// Memoized version
const fibCache: Map<number, number> = new Map();
function fibonacciMemo(n: number): number {
    if (n <= 1) return n;
    if (fibCache.has(n)) {
        return fibCache.get(n)!;
    }
    const result = fibonacciMemo(n - 1) + fibonacciMemo(n - 2);
    fibCache.set(n, result);
    return result;
}

console.log(fibonacciMemo(8));  // 21

// Generator version
function* fibonacciGenerator(): Generator<number> {
    let a = 0, b = 1;
    while (true) {
        yield a;
        [a, b] = [b, a + b];
    }
}

const fibGen = fibonacciGenerator();
for (let i = 0; i < 10; i++) {
    console.log(fibGen.next().value);
}
Coding Round
43. FizzBuzz

FizzBuzz using if-else or switch with TypeScript union types.

  • Modulo: i % 15 === 0
  • Order: Check 15 first
  • Return type: string[]
  • Union type: type FizzBuzzResult = "FizzBuzz" | "Fizz" | "Buzz" | string
typescript
// FizzBuzz in TypeScript
function fizzbuzz(n: number): void {
    for (let i = 1; i <= n; i++) {
        if (i % 15 === 0) {
            console.log("FizzBuzz");
        } else if (i % 3 === 0) {
            console.log("Fizz");
        } else if (i % 5 === 0) {
            console.log("Buzz");
        } else {
            console.log(i);
        }
    }
}

fizzbuzz(15);

// Return as array
function fizzbuzzArray(n: number): string[] {
    return Array.from({ length: n }, (_, i) => {
        const num = i + 1;
        if (num % 15 === 0) return "FizzBuzz";
        if (num % 3 === 0) return "Fizz";
        if (num % 5 === 0) return "Buzz";
        return String(num);
    });
}

console.log(fizzbuzzArray(15));

// With type safety
type FizzBuzzResult = "FizzBuzz" | "Fizz" | "Buzz" | string;

function fizzbuzzTyped(n: number): FizzBuzzResult[] {
    return Array.from({ length: n }, (_, i) => {
        const num = i + 1;
        if (num % 15 === 0) return "FizzBuzz";
        if (num % 3 === 0) return "Fizz";
        if (num % 5 === 0) return "Buzz";
        return String(num);
    });
}
Coding Round
44. Find missing number

Find missing number using formula or XOR method with TypeScript.

  • Formula: total - sum
  • XOR: XOR all numbers and indices
  • Return type: number
  • Edge cases: Empty array, missing first or last
typescript
// Find missing number in TypeScript
function findMissing(arr: number[]): number {
    const n = arr.length + 1;
    const total = n * (n + 1) / 2;
    const sum = arr.reduce((a, b) => a + b, 0);
    return total - sum;
}

console.log(findMissing([1, 2, 4, 5, 6]));  // 3

// Using XOR
function findMissingXOR(arr: number[]): number {
    const n = arr.length + 1;
    let xorSum = 0;
    for (let i = 1; i <= n; i++) {
        xorSum ^= i;
    }
    for (const num of arr) {
        xorSum ^= num;
    }
    return xorSum;
}

console.log(findMissingXOR([1, 2, 4, 5, 6]));  // 3

// With type safety
function findMissingTyped(arr: number[]): number | null {
    if (arr.length === 0) return null;
    const n = arr.length + 1;
    const total = n * (n + 1) / 2;
    const sum = arr.reduce((a, b) => a + b, 0);
    return total - sum;
}
Coding Round
45. Find duplicates

Find duplicates using Set or filter method with TypeScript generics.

  • Set: new Set()
  • Filter: arr.filter((item, index) => arr.indexOf(item) !== index)
  • Generic: <T>(arr: T[]): T[]
  • Complexity: O(n) time
typescript
// Find duplicates in TypeScript
function findDuplicates<T>(arr: T[]): T[] {
    const seen = new Set<T>();
    const duplicates = new Set<T>();
    for (const item of arr) {
        if (seen.has(item)) {
            duplicates.add(item);
        } else {
            seen.add(item);
        }
    }
    return Array.from(duplicates);
}

console.log(findDuplicates([1, 2, 3, 2, 4, 3]));  // [2, 3]

// Using filter
function findDuplicatesFilter<T>(arr: T[]): T[] {
    return arr.filter((item, index) => arr.indexOf(item) !== index);
}

console.log(findDuplicatesFilter([1, 2, 3, 2, 4, 3]));  // [2, 3]

// Using Map
function findDuplicatesMap<T>(arr: T[]): T[] {
    const counts = new Map<T, number>();
    const duplicates: T[] = [];
    for (const item of arr) {
        const count = counts.get(item) || 0;
        counts.set(item, count + 1);
    }
    for (const [item, count] of counts) {
        if (count > 1) {
            duplicates.push(item);
        }
    }
    return duplicates;
}

// Type-safe with generic constraint
function findDuplicatesTyped<T extends string | number>(arr: T[]): T[] {
    const seen = new Set<T>();
    const duplicates = new Set<T>();
    for (const item of arr) {
        if (seen.has(item)) {
            duplicates.add(item);
        } else {
            seen.add(item);
        }
    }
    return Array.from(duplicates);
}
Coding Round
46. Sum of array

Calculate sum using reduce or manual iteration with TypeScript.

  • reduce: arr.reduce((a, b) => a + b, 0)
  • Manual: Iterate and accumulate
  • Return type: number
  • Generic: <T extends number>(arr: T[]): T
typescript
// Sum of array in TypeScript
function sumArray(arr: number[]): number {
    return arr.reduce((a, b) => a + b, 0);
}

console.log(sumArray([1, 2, 3, 4, 5]));  // 15

// Manual implementation
function sumArrayManual(arr: number[]): number {
    let total = 0;
    for (const num of arr) {
        total += num;
    }
    return total;
}

console.log(sumArrayManual([1, 2, 3, 4, 5]));  // 15

// Using forEach
function sumArrayForEach(arr: number[]): number {
    let total = 0;
    arr.forEach(num => total += num);
    return total;
}

// Generic sum for numeric types
function sumGeneric<T extends number>(arr: T[]): T {
    return arr.reduce((a, b) => (a + b) as T, 0 as T);
}

// With type safety
function sumArraySafe(arr: number[]): number {
    if (arr.length === 0) return 0;
    return arr.reduce((a, b) => a + b, 0);
}
Coding Round
47. Average of array

Calculate average using sum divided by count with TypeScript.

  • Method: sum / arr.length
  • Empty array: Return 0
  • Return type: number
  • Precision: Returns floating point
typescript
// Average of array in TypeScript
function averageArray(arr: number[]): number {
    if (arr.length === 0) return 0;
    return arr.reduce((a, b) => a + b, 0) / arr.length;
}

console.log(averageArray([1, 2, 3, 4, 5]));  // 3

// Manual implementation
function averageArrayManual(arr: number[]): number {
    if (arr.length === 0) return 0;
    let total = 0;
    for (const num of arr) {
        total += num;
    }
    return total / arr.length;
}

console.log(averageArrayManual([1, 2, 3, 4, 5]));  // 3

// Using forEach
function averageArrayForEach(arr: number[]): number {
    if (arr.length === 0) return 0;
    let total = 0;
    arr.forEach(num => total += num);
    return total / arr.length;
}

// With floating point precision
function averageArrayPrecision(arr: number[]): number {
    if (arr.length === 0) return 0;
    const sum = arr.reduce((a, b) => a + b, 0);
    return Number((sum / arr.length).toFixed(2));
}
Coding Round
48. Sort array ascending

Sort using sort with comparison function and TypeScript generics.

  • sort: arr.sort((a, b) => a - b)
  • Generic: <T>(arr: T[]): T[]
  • Strings: arr.sort((a, b) => a.localeCompare(b))
  • Complexity: O(n log n)
typescript
// Sort array ascending in TypeScript
function sortAscending<T>(arr: T[]): T[] {
    return [...arr].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
}

console.log(sortAscending([5, 2, 8, 1, 9]));  // [1, 2, 5, 8, 9]

// For numbers
function sortAscendingNumbers(arr: number[]): number[] {
    return [...arr].sort((a, b) => a - b);
}

console.log(sortAscendingNumbers([5, 2, 8, 1, 9]));  // [1, 2, 5, 8, 9]

// For strings
function sortAscendingStrings(arr: string[]): string[] {
    return [...arr].sort((a, b) => a.localeCompare(b));
}

console.log(sortAscendingStrings(["banana", "apple", "cherry"]));  // ["apple", "banana", "cherry"]

// In-place sorting
function sortAscendingInPlace<T>(arr: T[]): T[] {
    return arr.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
}

// Generic sorting
function sortAscendingGeneric<T>(arr: T[], compareFn?: (a: T, b: T) => number): T[] {
    if (compareFn) {
        return [...arr].sort(compareFn);
    }
    return [...arr].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
}
Coding Round
49. Sort array descending

Sort descending by reversing comparison with TypeScript generics.

  • sort: arr.sort((a, b) => b - a)
  • Generic: <T>(arr: T[]): T[]
  • Strings: arr.sort((a, b) => b.localeCompare(a))
  • Complexity: O(n log n)
typescript
// Sort array descending in TypeScript
function sortDescending<T>(arr: T[]): T[] {
    return [...arr].sort((a, b) => a > b ? -1 : a < b ? 1 : 0);
}

console.log(sortDescending([5, 2, 8, 1, 9]));  // [9, 8, 5, 2, 1]

// For numbers
function sortDescendingNumbers(arr: number[]): number[] {
    return [...arr].sort((a, b) => b - a);
}

console.log(sortDescendingNumbers([5, 2, 8, 1, 9]));  // [9, 8, 5, 2, 1]

// For strings
function sortDescendingStrings(arr: string[]): string[] {
    return [...arr].sort((a, b) => b.localeCompare(a));
}

console.log(sortDescendingStrings(["banana", "apple", "cherry"]));  // ["cherry", "banana", "apple"]

// In-place sorting
function sortDescendingInPlace<T>(arr: T[]): T[] {
    return arr.sort((a, b) => a > b ? -1 : a < b ? 1 : 0);
}

// Generic with compare function
function sortDescendingGeneric<T>(arr: T[], compareFn?: (a: T, b: T) => number): T[] {
    if (compareFn) {
        return [...arr].sort((a, b) => -compareFn(a, b));
    }
    return [...arr].sort((a, b) => a > b ? -1 : a < b ? 1 : 0);
}
Coding Round
50. Flatten nested array

Flatten nested arrays using recursion, flat, or reduce with TypeScript.

  • Recursive: Check if element is array
  • flat: arr.flat(Infinity)
  • reduce: reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val), [])
  • Generic: <T>(arr: any[]): T[]
typescript
// Flatten nested array in TypeScript
function flattenArray<T>(arr: any[]): T[] {
    const result: T[] = [];
    for (const item of arr) {
        if (Array.isArray(item)) {
            result.push(...flattenArray(item));
        } else {
            result.push(item);
        }
    }
    return result;
}

console.log(flattenArray([1, [2, [3, 4], 5], 6]));  // [1, 2, 3, 4, 5, 6]

// Using reduce
function flattenArrayReduce<T>(arr: any[]): T[] {
    return arr.reduce((acc: T[], val: any) => {
        if (Array.isArray(val)) {
            acc.push(...flattenArrayReduce(val));
        } else {
            acc.push(val);
        }
        return acc;
    }, []);
}

console.log(flattenArrayReduce([1, [2, [3, 4], 5], 6]));  // [1, 2, 3, 4, 5, 6]

// Using flat (modern JavaScript)
function flattenArrayFlat<T>(arr: any[]): T[] {
    return arr.flat(Infinity);
}

console.log(flattenArrayFlat([1, [2, [3, 4], 5], 6]));  // [1, 2, 3, 4, 5, 6]

// Type-safe flatten
function flattenTyped<T>(arr: T[] | any[]): T[] {
    const result: T[] = [];
    for (const item of arr) {
        if (Array.isArray(item)) {
            result.push(...flattenTyped(item));
        } else {
            result.push(item);
        }
    }
    return result;
}
Coding Round
51. Chunk array

Split array into chunks using slice in loop with TypeScript.

  • Loop: Iterate with step size
  • slice: arr.slice(i, i + size)
  • Generic: <T>(arr: T[], size: number): T[][]
  • Edge case: Handle last chunk
typescript
// Chunk array in TypeScript
function chunkArray<T>(arr: T[], size: number): T[][] {
    const chunks: T[][] = [];
    for (let i = 0; i < arr.length; i += size) {
        chunks.push(arr.slice(i, i + size));
    }
    return chunks;
}

console.log(chunkArray([1, 2, 3, 4, 5, 6], 2));  // [[1, 2], [3, 4], [5, 6]]

// Using while loop
function chunkArrayWhile<T>(arr: T[], size: number): T[][] {
    const chunks: T[][] = [];
    let i = 0;
    while (i < arr.length) {
        chunks.push(arr.slice(i, i + size));
        i += size;
    }
    return chunks;
}

console.log(chunkArrayWhile([1, 2, 3, 4, 5, 6], 2));  // [[1, 2], [3, 4], [5, 6]]

// With padding
function chunkArrayPadding<T>(arr: T[], size: number, padValue: T): T[][] {
    const chunks = chunkArray(arr, size);
    const lastChunk = chunks[chunks.length - 1];
    if (lastChunk && lastChunk.length < size) {
        while (lastChunk.length < size) {
            lastChunk.push(padValue);
        }
    }
    return chunks;
}

console.log(chunkArrayPadding([1, 2, 3, 4, 5], 3, 0));  // [[1, 2, 3], [4, 5, 0]]

// Type-safe chunk
function chunkTyped<T>(arr: T[], size: number): T[][] {
    if (size <= 0) throw new Error("Size must be greater than 0");
    const chunks: T[][] = [];
    for (let i = 0; i < arr.length; i += size) {
        chunks.push(arr.slice(i, Math.min(i + size, arr.length)));
    }
    return chunks;
}
Coding Round
53. Quick sort

Quick sort using recursion and partitioning with TypeScript generics.

  • Algorithm: Choose pivot, partition, recurse
  • Generic: <T>(arr: T[]): T[]
  • In-place: quickSortInPlace<T>(arr: T[], low: number, high: number): void
  • Comparator: quickSortWithComparator<T>(arr: T[], comparator: (a: T, b: T) => number): T[]
typescript
// Quick sort in TypeScript
function quickSort<T>(arr: T[]): T[] {
    if (arr.length <= 1) return arr;
    const pivot = arr[0];
    const left: T[] = [];
    const right: T[] = [];
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] < pivot) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return [...quickSort(left), pivot, ...quickSort(right)];
}

console.log(quickSort([5, 3, 8, 4, 2, 7, 1, 6]));  // [1, 2, 3, 4, 5, 6, 7, 8]

// In-place quick sort
function quickSortInPlace<T>(arr: T[], low: number = 0, high: number = arr.length - 1): void {
    if (low < high) {
        const pi = partition(arr, low, high);
        quickSortInPlace(arr, low, pi - 1);
        quickSortInPlace(arr, pi + 1, high);
    }
}

function partition<T>(arr: T[], low: number, high: number): number {
    const pivot = arr[high];
    let i = low - 1;
    for (let j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
    }
    [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
    return i + 1;
}

const numbers = [5, 3, 8, 4, 2, 7, 1, 6];
quickSortInPlace(numbers);
console.log(numbers);  // [1, 2, 3, 4, 5, 6, 7, 8]

// Generic with comparator
function quickSortWithComparator<T>(arr: T[], comparator: (a: T, b: T) => number): T[] {
    if (arr.length <= 1) return arr;
    const pivot = arr[0];
    const left: T[] = [];
    const right: T[] = [];
    for (let i = 1; i < arr.length; i++) {
        if (comparator(arr[i], pivot) < 0) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return [...quickSortWithComparator(left, comparator), pivot, ...quickSortWithComparator(right, comparator)];
}
Coding Round
54. Merge sort

Merge sort using divide-and-conquer and merging with TypeScript generics.

  • Algorithm: Divide, sort, merge
  • Generic: <T>(arr: T[]): T[]
  • Comparator: mergeSortWithComparator<T>(arr: T[], comparator: (a: T, b: T) => number): T[]
  • Complexity: O(n log n)
typescript
// Merge sort in TypeScript
function mergeSort<T>(arr: T[]): T[] {
    if (arr.length <= 1) return arr;
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    return merge(left, right);
}

function merge<T>(left: T[], right: T[]): T[] {
    const result: T[] = [];
    let i = 0, j = 0;
    while (i < left.length && j < right.length) {
        if (left[i] <= right[j]) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    while (i < left.length) {
        result.push(left[i++]);
    }
    while (j < right.length) {
        result.push(right[j++]);
    }
    return result;
}

console.log(mergeSort([5, 3, 8, 4, 2, 7, 1, 6]));  // [1, 2, 3, 4, 5, 6, 7, 8]

// Generic with comparator
function mergeSortWithComparator<T>(arr: T[], comparator: (a: T, b: T) => number): T[] {
    if (arr.length <= 1) return arr;
    const mid = Math.floor(arr.length / 2);
    const left = mergeSortWithComparator(arr.slice(0, mid), comparator);
    const right = mergeSortWithComparator(arr.slice(mid), comparator);
    return mergeWithComparator(left, right, comparator);
}

function mergeWithComparator<T>(left: T[], right: T[], comparator: (a: T, b: T) => number): T[] {
    const result: T[] = [];
    let i = 0, j = 0;
    while (i < left.length && j < right.length) {
        if (comparator(left[i], right[j]) <= 0) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    while (i < left.length) {
        result.push(left[i++]);
    }
    while (j < right.length) {
        result.push(right[j++]);
    }
    return result;
}

const names = ["Charlie", "Alice", "Bob", "David"];
console.log(mergeSortWithComparator(names, (a, b) => a.localeCompare(b)));  // ["Alice", "Bob", "Charlie", "David"]
Coding Round
55. Bubble sort

Bubble sort with early termination using TypeScript generics.

  • Algorithm: Compare adjacent, swap
  • Generic: <T>(arr: T[]): T[]
  • Optimized: bubbleSortOptimized<T>(arr: T[]): T[]
  • Comparator: bubbleSortGeneric<T>(arr: T[], comparator: (a: T, b: T) => number): T[]
typescript
// Bubble sort in TypeScript
function bubbleSort<T>(arr: T[]): T[] {
    const sorted = [...arr];
    for (let i = 0; i < sorted.length - 1; i++) {
        for (let j = 0; j < sorted.length - 1 - i; j++) {
            if (sorted[j] > sorted[j + 1]) {
                [sorted[j], sorted[j + 1]] = [sorted[j + 1], sorted[j]];
            }
        }
    }
    return sorted;
}

console.log(bubbleSort([5, 3, 8, 4, 2, 7, 1, 6]));  // [1, 2, 3, 4, 5, 6, 7, 8]

// Optimized bubble sort
function bubbleSortOptimized<T>(arr: T[]): T[] {
    const sorted = [...arr];
    let swapped = true;
    for (let i = 0; i < sorted.length - 1 && swapped; i++) {
        swapped = false;
        for (let j = 0; j < sorted.length - 1 - i; j++) {
            if (sorted[j] > sorted[j + 1]) {
                [sorted[j], sorted[j + 1]] = [sorted[j + 1], sorted[j]];
                swapped = true;
            }
        }
    }
    return sorted;
}

console.log(bubbleSortOptimized([5, 3, 8, 4, 2, 7, 1, 6]));  // [1, 2, 3, 4, 5, 6, 7, 8]

// Generic with comparator
function bubbleSortGeneric<T>(arr: T[], comparator: (a: T, b: T) => number): T[] {
    const sorted = [...arr];
    for (let i = 0; i < sorted.length - 1; i++) {
        for (let j = 0; j < sorted.length - 1 - i; j++) {
            if (comparator(sorted[j], sorted[j + 1]) > 0) {
                [sorted[j], sorted[j + 1]] = [sorted[j + 1], sorted[j]];
            }
        }
    }
    return sorted;
}
Coding Round
56. Intersection of arrays

Find common elements using Set or filter with TypeScript generics.

  • Set: new Set(arr2) and filter
  • Generic: <T>(arr1: T[], arr2: T[]): T[]
  • Type constraint: <T extends string | number>
  • Complexity: O(n) time with Set
typescript
// Intersection of arrays in TypeScript
function intersection<T>(arr1: T[], arr2: T[]): T[] {
    const set2 = new Set(arr2);
    return arr1.filter(item => set2.has(item));
}

console.log(intersection([1, 2, 3, 4], [3, 4, 5, 6]));  // [3, 4]

// Using Set
function intersectionSet<T>(arr1: T[], arr2: T[]): T[] {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    return Array.from(set1).filter(item => set2.has(item));
}

console.log(intersectionSet([1, 2, 3, 4], [3, 4, 5, 6]));  // [3, 4]

// Using filter with includes
function intersectionFilter<T>(arr1: T[], arr2: T[]): T[] {
    return arr1.filter(item => arr2.includes(item));
}

console.log(intersectionFilter([1, 2, 3, 4], [3, 4, 5, 6]));  // [3, 4]

// Generic with type safety
function intersectionTyped<T extends string | number>(arr1: T[], arr2: T[]): T[] {
    const set2 = new Set(arr2);
    return arr1.filter((item): item is T => set2.has(item));
}
Coding Round
57. Union of arrays

Combine arrays with unique elements using Set with TypeScript generics.

  • Set: new Set([...arr1, ...arr2])
  • Generic: <T>(arr1: T[], arr2: T[]): T[]
  • Preserve order: arr1.filter(item => !arr2.includes(item))
  • Complexity: O(n) time
typescript
// Union of arrays in TypeScript
function union<T>(arr1: T[], arr2: T[]): T[] {
    return [...new Set([...arr1, ...arr2])];
}

console.log(union([1, 2, 3], [3, 4, 5]));  // [1, 2, 3, 4, 5]

// Preserving order
function unionOrder<T>(arr1: T[], arr2: T[]): T[] {
    const result = [...arr1];
    for (const item of arr2) {
        if (!result.includes(item)) {
            result.push(item);
        }
    }
    return result;
}

console.log(unionOrder([1, 2, 3], [3, 4, 5]));  // [1, 2, 3, 4, 5]

// Using filter
function unionFilter<T>(arr1: T[], arr2: T[]): T[] {
    return [...arr1, ...arr2.filter(item => !arr1.includes(item))];
}

console.log(unionFilter([1, 2, 3], [3, 4, 5]));  // [1, 2, 3, 4, 5]

// Generic with type safety
function unionTyped<T extends string | number>(arr1: T[], arr2: T[]): T[] {
    return Array.from(new Set([...arr1, ...arr2]));
}
Coding Round
58. Difference of arrays

Find elements in first array not in second using Set with TypeScript.

  • Set: new Set(arr2) and filter
  • Generic: <T>(arr1: T[], arr2: T[]): T[]
  • Symmetric difference: arr1.filter(x => !set2.has(x)).concat(arr2.filter(x => !set1.has(x)))
  • Complexity: O(n) time
typescript
// Difference of arrays in TypeScript
function difference<T>(arr1: T[], arr2: T[]): T[] {
    const set2 = new Set(arr2);
    return arr1.filter(item => !set2.has(item));
}

console.log(difference([1, 2, 3, 4], [3, 4, 5, 6]));  // [1, 2]

// Symmetric difference
function symmetricDifference<T>(arr1: T[], arr2: T[]): T[] {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    const result: T[] = [];
    for (const item of set1) {
        if (!set2.has(item)) result.push(item);
    }
    for (const item of set2) {
        if (!set1.has(item)) result.push(item);
    }
    return result;
}

console.log(symmetricDifference([1, 2, 3], [3, 4, 5]));  // [1, 2, 4, 5]

// Using filter
function differenceFilter<T>(arr1: T[], arr2: T[]): T[] {
    return arr1.filter(item => !arr2.includes(item));
}

console.log(differenceFilter([1, 2, 3, 4], [3, 4, 5, 6]));  // [1, 2]

// Generic with type safety
function differenceTyped<T extends string | number>(arr1: T[], arr2: T[]): T[] {
    const set2 = new Set(arr2);
    return arr1.filter((item): item is T => !set2.has(item));
}
Coding Round
59. Group by property

Group objects by property using Map or reduce with TypeScript.

  • Map: new Map<T[keyof T], T[]>()
  • Generic: <T extends Record<string, any>>(items: T[], key: keyof T): Map<T[keyof T], T[]>
  • reduce: items.reduce((groups, item) => { ... }, {})
  • Complexity: O(n) time
typescript
// Group by property in TypeScript
interface Item {
    type: string;
    name: string;
}

function groupByProperty<T extends Record<string, any>>(items: T[], key: keyof T): Map<T[keyof T], T[]> {
    const groups = new Map<T[keyof T], T[]>();
    for (const item of items) {
        const keyValue = item[key];
        if (!groups.has(keyValue)) {
            groups.set(keyValue, []);
        }
        groups.get(keyValue)!.push(item);
    }
    return groups;
}

// Usage
const data: Item[] = [
    { type: "fruit", name: "apple" },
    { type: "fruit", name: "banana" },
    { type: "veg", name: "carrot" }
];

const groups = groupByProperty(data, "type");
for (const [key, items] of groups) {
    console.log(`${key}: ${items.map(i => i.name).join(", ")}`);
}

// Using reduce
function groupByPropertyReduce<T extends Record<string, any>>(items: T[], key: keyof T): Record<string, T[]> {
    return items.reduce((groups: Record<string, T[]>, item: T) => {
        const keyValue = String(item[key]);
        if (!groups[keyValue]) {
            groups[keyValue] = [];
        }
        groups[keyValue].push(item);
        return groups;
    }, {});
}

// Generic group by
function groupBy<T, K extends keyof T>(items: T[], key: K): Map<T[K], T[]> {
    return items.reduce((map, item) => {
        const keyValue = item[key];
        if (!map.has(keyValue)) {
            map.set(keyValue, []);
        }
        map.get(keyValue)!.push(item);
        return map;
    }, new Map<T[K], T[]>());
}
Coding Round
60. Deep clone object

Deep clone using recursion, JSON methods, or structuredClone with TypeScript.

  • JSON: JSON.parse(JSON.stringify(obj))
  • Recursive: deepClone<T>(obj: T): T
  • structuredClone: structuredClone(obj)
  • Type safety: Generic type preservation
typescript
// Deep clone object in TypeScript
function deepClone<T>(obj: T): T {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map(item => deepClone(item)) as any;
    }
    const cloned: any = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloned[key] = deepClone(obj[key]);
        }
    }
    return cloned;
}

// Usage
interface User {
    name: string;
    address: {
        city: string;
        zip: string;
    };
}

const original: User = {
    name: "Alice",
    address: {
        city: "NYC",
        zip: "10001"
    }
};

const cloned = deepClone(original);
cloned.name = "Bob";
cloned.address.city = "LA";

console.log(original.name);  // Alice
console.log(cloned.name);    // Bob
console.log(original.address.city);  // NYC
console.log(cloned.address.city);    // LA

// Using JSON methods (shallow)
function deepCloneJSON<T>(obj: T): T {
    return JSON.parse(JSON.stringify(obj));
}

// Using structuredClone (modern browsers)
function deepCloneStructured<T>(obj: T): T {
    return structuredClone(obj);
}

// Type-safe deep clone
function deepCloneTyped<T>(obj: T): T {
    if (obj === null || typeof obj !== "object") return obj;
    if (Array.isArray(obj)) return obj.map(item => deepCloneTyped(item)) as any;
    const cloned: any = {};
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            cloned[key] = deepCloneTyped(obj[key]);
        }
    }
    return cloned;
}
Coding Round
61. Immutable update

Perform immutable updates using spread or Object.assign with TypeScript.

  • Spread: {...obj, [key]: value}
  • Generic: updateImmutable<T>(obj: T, path: string, value: any): T
  • Nested: Recursive spread updates
  • Type safety: Preserve object types
typescript
// Immutable update in TypeScript
interface User {
    name: string;
    age: number;
}

interface State {
    user: User;
}

function updateImmutable<T>(obj: T, path: string, value: any): T {
    const parts = path.split('.');
    if (parts.length === 1) {
        return { ...obj, [parts[0]]: value };
    }
    const first = parts[0];
    const rest = parts.slice(1).join('.');
    const nested = (obj as any)[first];
    return { ...obj, [first]: updateImmutable(nested, rest, value) };
}

// Usage
const state: State = {
    user: {
        name: "Alice",
        age: 25
    }
};

const newState = updateImmutable(state, "user.age", 26);
console.log(state.user.age);  // 25
console.log(newState.user.age);  // 26

// Using spread operator for nested objects
function updateUser(state: State, updates: Partial<User>): State {
    return {
        ...state,
        user: {
            ...state.user,
            ...updates
        }
    };
}

const newState2 = updateUser(state, { age: 27 });
console.log(newState2.user.age);  // 27

// With immer library (alternative)
// import produce from 'immer';
// const newState3 = produce(state, draft => {
//     draft.user.age = 28;
// });

// Generic immutable update
function updateImmutableGeneric<T extends Record<string, any>>(
    obj: T,
    key: keyof T,
    value: any
): T {
    return { ...obj, [key]: value };
}
Coding Round
62. Pipe function

Pipe composes functions from left to right with TypeScript generics.

  • Implementation: pipe<T>(value: T, ...fns: ((arg: T) => T)[]): T
  • Generic: Type-safe function composition
  • Async: pipeAsync<T>(value: T, ...fns: ((arg: T) => Promise<T>)[]): Promise<T>
  • Direction: Left to right
typescript
// Pipe function in TypeScript
function pipe<T>(value: T, ...fns: ((arg: T) => T)[]): T {
    return fns.reduce((acc, fn) => fn(acc), value);
}

// Usage
const double = (x: number): number => x * 2;
const addTen = (x: number): number => x + 10;
const square = (x: number): number => x * x;

const result = pipe(5, double, addTen, square);
console.log(result);  // (5*2+10)^2 = 400

// With different types
function pipeTyped<T, U, V>(value: T, fn1: (arg: T) => U, fn2: (arg: U) => V): V {
    return fn2(fn1(value));
}

const resultTyped = pipeTyped(5, double, addTen);
console.log(resultTyped);  // 20

// Async pipe
async function pipeAsync<T>(value: T, ...fns: ((arg: T) => Promise<T>)[]): Promise<T> {
    let result = value;
    for (const fn of fns) {
        result = await fn(result);
    }
    return result;
}

const asyncDouble = async (x: number): Promise<number> => x * 2;
const asyncAddTen = async (x: number): Promise<number> => x + 10;

pipeAsync(5, asyncDouble, asyncAddTen).then(result => console.log(result));  // 20

// Pipe with custom operator
function pipeOperator<T>(...fns: ((arg: T) => T)[]): (arg: T) => T {
    return (value: T) => fns.reduce((acc, fn) => fn(acc), value);
}

const process = pipeOperator(double, addTen, square);
console.log(process(5));  // 400
Coding Round
63. Compose function

Compose functions from right to left with TypeScript generics.

  • Implementation: compose<T>(...fns: ((arg: T) => T)[]): (arg: T) => T
  • Generic: Type-safe function composition
  • Async: composeAsync<T>(...fns: ((arg: T) => Promise<T>)[]): (arg: T) => Promise<T>
  • Direction: Right to left
typescript
// Compose function in TypeScript
function compose<T>(...fns: ((arg: T) => T)[]): (arg: T) => T {
    return (value: T) => fns.reduceRight((acc, fn) => fn(acc), value);
}

// Usage
const double = (x: number): number => x * 2;
const addTen = (x: number): number => x + 10;
const square = (x: number): number => x * x;

const process = compose(square, addTen, double);
console.log(process(5));  // (5*2+10)^2 = 400

// With different types
function composeTyped<T, U, V>(fn1: (arg: U) => V, fn2: (arg: T) => U): (arg: T) => V {
    return (value: T) => fn1(fn2(value));
}

const processTyped = composeTyped(addTen, double);
console.log(processTyped(5));  // 20

// Async compose
async function composeAsync<T>(...fns: ((arg: T) => Promise<T>)[]): (arg: T) => Promise<T> {
    return async (value: T) => {
        let result = value;
        for (let i = fns.length - 1; i >= 0; i--) {
            result = await fns[i](result);
        }
        return result;
    };
}

const asyncSquare = async (x: number): Promise<number> => x * x;
const asyncAddTen = async (x: number): Promise<number> => x + 10;

const processAsync = composeAsync(asyncSquare, asyncAddTen);
processAsync(5).then(result => console.log(result));  // (5+10)^2 = 225

// Compose with custom operator
function composeOperator<T>(...fns: ((arg: T) => T)[]): (arg: T) => T {
    return (value: T) => fns.reduceRight((acc, fn) => fn(acc), value);
}
Coding Round
64. Memoization

Cache function results based on arguments using Map with TypeScript.

  • Cache: Map<string, ReturnType<T>>
  • Generic: memoize<T extends (...args: any[]) => any>(fn: T): T
  • TTL: memoizeWithTTL<T>(fn: T, ttl: number): T
  • Trade-off: Memory for speed
typescript
// Memoization in TypeScript
function memoize<T extends (...args: any[]) => any>(fn: T): T {
    const cache = new Map<string, ReturnType<T>>();
    return ((...args: Parameters<T>) => {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    }) as T;
}

// Fibonacci with memoization
const fibMemo = memoize((n: number): number => {
    if (n <= 1) return n;
    return fibMemo(n - 1) + fibMemo(n - 2);
});

console.log(fibMemo(10));  // 55

// Memoize with multiple arguments
function memoizeMulti<T extends (...args: any[]) => any>(fn: T): T {
    const cache = new Map<string, ReturnType<T>>();
    return ((...args: Parameters<T>) => {
        const key = args.map(arg => JSON.stringify(arg)).join('|');
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    }) as T;
}

const addMemo = memoizeMulti((a: number, b: number): number => {
    console.log(`Computing: ${a} + ${b}`);
    return a + b;
});

console.log(addMemo(5, 3));  // Computes
console.log(addMemo(5, 3));  // Returns cached

// Memoize with TTL
function memoizeWithTTL<T extends (...args: any[]) => any>(fn: T, ttl: number): T {
    const cache = new Map<string, { value: ReturnType<T>; timestamp: number }>();
    return ((...args: Parameters<T>) => {
        const key = JSON.stringify(args);
        const cached = cache.get(key);
        if (cached && Date.now() - cached.timestamp < ttl) {
            return cached.value;
        }
        const result = fn(...args);
        cache.set(key, { value: result, timestamp: Date.now() });
        return result;
    }) as T;
}

const expensiveFn = memoizeWithTTL((n: number): number => {
    console.log(`Computing expensive: ${n}`);
    return n * n;
}, 5000);

console.log(expensiveFn(5));  // Computes
console.log(expensiveFn(5));  // Returns cached (within TTL)
Coding Round
65. Once function

Ensure a function is called only once using closure with TypeScript.

  • Closure: let called = false
  • Generic: once<T extends (...args: any[]) => any>(fn: T): T
  • Async: onceAsync<T extends (...args: any[]) => Promise<any>>(fn: T): T
  • Use case: Initialization
typescript
// Once function in TypeScript
function once<T extends (...args: any[]) => any>(fn: T): T {
    let called = false;
    let result: ReturnType<T>;
    return ((...args: Parameters<T>) => {
        if (!called) {
            called = true;
            result = fn(...args);
        }
        return result;
    }) as T;
}

// Usage
const initialize = once(() => {
    console.log("Initialized");
    return { id: 1, name: "App" };
});

console.log(initialize());  // Prints "Initialized"
console.log(initialize());  // Returns cached result

// Once with async function
function onceAsync<T extends (...args: any[]) => Promise<any>>(fn: T): T {
    let called = false;
    let result: Promise<ReturnType<T>>;
    return ((...args: Parameters<T>) => {
        if (!called) {
            called = true;
            result = fn(...args);
        }
        return result;
    }) as T;
}

const initializeAsync = onceAsync(async () => {
    console.log("Initializing async");
    await new Promise(resolve => setTimeout(resolve, 1000));
    return { id: 2, name: "App2" };
});

initializeAsync().then(res => console.log(res));
initializeAsync().then(res => console.log(res));  // Returns cached promise

// Once with class
class Once<T> {
    private called = false;
    private result: T;
    private fn: () => T;
    
    constructor(fn: () => T) {
        this.fn = fn;
    }
    
    call(): T {
        if (!this.called) {
            this.called = true;
            this.result = this.fn();
        }
        return this.result;
    }
}

const onceInstance = new Once(() => {
    console.log("Initialized 2");
    return "Hello";
});

console.log(onceInstance.call());  // Prints "Initialized 2"
console.log(onceInstance.call());  // Returns cached
Coding Round
66. Debounce with leading edge

Debounce with leading edge using timer and timestamp with TypeScript.

  • Timer: setTimeout for delayed execution
  • Generic: debounceLeading<T extends (...args: any[]) => any>(delay: number, fn: T): (...args: Parameters<T>) => void
  • Async: debounceLeadingAsync<T extends (...args: any[]) => Promise<any>>(delay: number, fn: T): (...args: Parameters<T>) => Promise<ReturnType<T>>
  • Use case: Search input, API calls
typescript
// Debounce with leading edge in TypeScript
function debounceLeading<T extends (...args: any[]) => any>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => void {
    let lastCall = 0;
    let timer: ReturnType<typeof setTimeout> | null = null;
    
    return (...args: Parameters<T>) => {
        const now = Date.now();
        if (now - lastCall < delay) {
            if (timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(() => {
                lastCall = Date.now();
                fn(...args);
            }, delay);
        } else {
            lastCall = now;
            fn(...args);
        }
    };
}

// Usage
const debounced = debounceLeading(1000, (message: string) => {
    console.log(`Executed: ${message}`);
});

debounced("First");  // Executes immediately
debounced("Second"); // Scheduled for later
debounced("Third");  // Scheduled for later

// Debounce with return value
function debounceLeadingWithReturn<T extends (...args: any[]) => any>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
    let lastCall = 0;
    let timer: ReturnType<typeof setTimeout> | null = null;
    let resolveList: ((value: ReturnType<T>) => void)[] = [];
    
    return (...args: Parameters<T>) => {
        return new Promise<ReturnType<T>>((resolve) => {
            const now = Date.now();
            if (now - lastCall < delay) {
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(() => {
                    lastCall = Date.now();
                    const result = fn(...args);
                    resolveList.forEach(r => r(result));
                    resolveList = [];
                }, delay);
                resolveList.push(resolve);
            } else {
                lastCall = now;
                const result = fn(...args);
                resolve(result);
            }
        });
    };
}

// Async debounce
function debounceLeadingAsync<T extends (...args: any[]) => Promise<any>>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
    let lastCall = 0;
    let timer: ReturnType<typeof setTimeout> | null = null;
    let pendingResolves: ((value: ReturnType<T>) => void)[] = [];
    
    return (...args: Parameters<T>) => {
        return new Promise<ReturnType<T>>((resolve) => {
            const now = Date.now();
            if (now - lastCall < delay) {
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(async () => {
                    lastCall = Date.now();
                    const result = await fn(...args);
                    pendingResolves.forEach(r => r(result));
                    pendingResolves = [];
                }, delay);
                pendingResolves.push(resolve);
            } else {
                lastCall = now;
                resolve(fn(...args));
            }
        });
    };
}
Coding Round
67. Throttle with leading edge

Throttle with leading edge using timestamp tracking with TypeScript.

  • Timestamp: Track last execution time
  • Generic: throttleLeading<T extends (...args: any[]) => any>(delay: number, fn: T): (...args: Parameters<T>) => void
  • Trailing: throttleTrailing<T extends (...args: any[]) => any>(delay: number, fn: T): (...args: Parameters<T>) => void
  • Use case: Scroll events, resize
typescript
// Throttle with leading edge in TypeScript
function throttleLeading<T extends (...args: any[]) => any>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => void {
    let lastCall = 0;
    
    return (...args: Parameters<T>) => {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            fn(...args);
        }
    };
}

// Usage
const throttled = throttleLeading(1000, (message: string) => {
    console.log(`Executed: ${message}`);
});

throttled("First");   // Executes
throttled("Second");  // Ignored (within 1 second)
throttled("Third");   // Ignored (within 1 second)

// Throttle with trailing edge
function throttleTrailing<T extends (...args: any[]) => any>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => void {
    let lastCall = 0;
    let timer: ReturnType<typeof setTimeout> | null = null;
    
    return (...args: Parameters<T>) => {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            fn(...args);
        } else if (!timer) {
            timer = setTimeout(() => {
                timer = null;
                lastCall = Date.now();
                fn(...args);
            }, delay - (now - lastCall));
        }
    };
}

const throttledTrailing = throttleTrailing(1000, (message: string) => {
    console.log(`Executed (trailing): ${message}`);
});

throttledTrailing("First");   // Executes
throttledTrailing("Second");  // Scheduled for later
throttledTrailing("Third");   // Scheduled for later

// Throttle with return value
function throttleLeadingWithReturn<T extends (...args: any[]) => any>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => ReturnType<T> | undefined {
    let lastCall = 0;
    let lastResult: ReturnType<T> | undefined;
    
    return (...args: Parameters<T>) => {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            lastResult = fn(...args);
        }
        return lastResult;
    };
}

// Async throttle
function throttleLeadingAsync<T extends (...args: any[]) => Promise<any>>(
    delay: number,
    fn: T
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
    let lastCall = 0;
    let pendingPromise: Promise<ReturnType<T>> | null = null;
    
    return (...args: Parameters<T>) => {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            pendingPromise = fn(...args);
            return pendingPromise;
        }
        return pendingPromise || Promise.reject(new Error("Throttled"));
    };
}
Coding Round
68. Deep equal

Deep equality comparison using recursion for nested structures with TypeScript.

  • Recursive: Compare nested structures
  • Generic: deepEqual<T>(obj1: T, obj2: T): boolean
  • Comparator: deepEqualWithComparator<T>(obj1: T, obj2: T, comparator: (a: any, b: any) => boolean): boolean
  • Type safety: Preserve object types
typescript
// Deep equal in TypeScript
function deepEqual<T>(obj1: T, obj2: T): boolean {
    if (obj1 === obj2) return true;
    if (obj1 === null || obj2 === null) return false;
    if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
    
    if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
    
    if (Array.isArray(obj1) && Array.isArray(obj2)) {
        if (obj1.length !== obj2.length) return false;
        for (let i = 0; i < obj1.length; i++) {
            if (!deepEqual(obj1[i], obj2[i])) return false;
        }
        return true;
    }
    
    const keys1 = Object.keys(obj1) as (keyof T)[];
    const keys2 = Object.keys(obj2) as (keyof T)[];
    if (keys1.length !== keys2.length) return false;
    
    for (const key of keys1) {
        if (!obj2.hasOwnProperty(key)) return false;
        if (!deepEqual(obj1[key], obj2[key])) return false;
    }
    return true;
}

// Usage
interface User {
    name: string;
    address: {
        city: string;
        zip: string;
    };
}

const obj1: User = {
    name: "Alice",
    address: {
        city: "NYC",
        zip: "10001"
    }
};

const obj2: User = {
    name: "Alice",
    address: {
        city: "NYC",
        zip: "10001"
    }
};

const obj3: User = {
    name: "Bob",
    address: {
        city: "LA",
        zip: "90001"
    }
};

console.log(deepEqual(obj1, obj2));  // true
console.log(deepEqual(obj1, obj3));  // false

// Deep equal with custom comparator
function deepEqualWithComparator<T>(
    obj1: T,
    obj2: T,
    comparator: (a: any, b: any) => boolean
): boolean {
    if (obj1 === obj2) return true;
    if (obj1 === null || obj2 === null) return false;
    if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
    
    if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
    
    if (Array.isArray(obj1) && Array.isArray(obj2)) {
        if (obj1.length !== obj2.length) return false;
        for (let i = 0; i < obj1.length; i++) {
            if (!deepEqualWithComparator(obj1[i], obj2[i], comparator)) return false;
        }
        return true;
    }
    
    const keys1 = Object.keys(obj1) as (keyof T)[];
    const keys2 = Object.keys(obj2) as (keyof T)[];
    if (keys1.length !== keys2.length) return false;
    
    for (const key of keys1) {
        if (!obj2.hasOwnProperty(key)) return false;
        if (!comparator(obj1[key], obj2[key])) return false;
    }
    return true;
}
Coding Round
69. Observable pattern

Observable pattern with subscribers and notifications using TypeScript generics.

  • Observable: class Observable<T>
  • Subscribe: subscribe(observer: Observer<T>): () => void
  • Subject: class Subject<T> extends Observable<T>
  • BehaviorSubject: class BehaviorSubject<T> extends Subject<T>
typescript
// Observable pattern in TypeScript
interface Observer<T> {
    next(value: T): void;
    error(error: any): void;
    complete(): void;
}

class Observable<T> {
    private observers: Observer<T>[] = [];
    private isCompleted: boolean = false;
    
    subscribe(observer: Observer<T>): () => void {
        if (this.isCompleted) {
            observer.complete();
            return () => {};
        }
        this.observers.push(observer);
        return () => {
            const index = this.observers.indexOf(observer);
            if (index !== -1) {
                this.observers.splice(index, 1);
            }
        };
    }
    
    next(value: T): void {
        if (this.isCompleted) return;
        for (const observer of this.observers) {
            observer.next(value);
        }
    }
    
    error(error: any): void {
        if (this.isCompleted) return;
        this.isCompleted = true;
        for (const observer of this.observers) {
            observer.error(error);
        }
        this.observers = [];
    }
    
    complete(): void {
        if (this.isCompleted) return;
        this.isCompleted = true;
        for (const observer of this.observers) {
            observer.complete();
        }
        this.observers = [];
    }
}

// Usage
const observable = new Observable<number>();
const unsubscribe = observable.subscribe({
    next: (value) => console.log(`Observer 1: ${value}`),
    error: (error) => console.log(`Error: ${error}`),
    complete: () => console.log("Completed")
});

observable.next(1);
observable.next(2);
unsubscribe();
observable.next(3); // Will not be received

// Subject (hot observable)
class Subject<T> extends Observable<T> {
    private value: T | undefined;
    
    next(value: T): void {
        this.value = value;
        super.next(value);
    }
    
    getValue(): T | undefined {
        return this.value;
    }
}

// BehaviorSubject
class BehaviorSubject<T> extends Subject<T> {
    constructor(initialValue: T) {
        super();
        this.value = initialValue;
    }
    
    getValue(): T {
        return this.value as T;
    }
}

// Usage
const subject = new Subject<string>();
subject.subscribe({
    next: (value) => console.log(`Subject: ${value}`)
});
subject.next("Hello");
console.log(subject.getValue());
Coding Round
70. Singleton pattern

Singleton pattern using private constructor and static instance with TypeScript.

  • Private constructor: private constructor() { }
  • Static instance: private static instance: Singleton
  • getInstance: static getInstance(): Singleton
  • Lazy initialization: Create on first call
typescript
// Singleton pattern in TypeScript
class Singleton {
    private static instance: Singleton;
    private data: string[] = [];
    
    private constructor() {}
    
    static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
    
    addData(item: string): void {
        this.data.push(item);
    }
    
    getData(): string[] {
        return this.data;
    }
}

// Usage
const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
singleton1.addData("Hello");
console.log(singleton2.getData()); // ["Hello"]
console.log(singleton1 === singleton2); // true

// Singleton with lazy initialization
class LazySingleton {
    private static instance: LazySingleton | null = null;
    private data: Map<string, any> = new Map();
    
    private constructor() {}
    
    static getInstance(): LazySingleton {
        if (!LazySingleton.instance) {
            LazySingleton.instance = new LazySingleton();
        }
        return LazySingleton.instance;
    }
}

// Singleton with module pattern
class ModuleSingleton {
    private static instance: ModuleSingleton;
    
    private constructor() {}
    
    static getInstance(): ModuleSingleton {
        return ModuleSingleton.instance || (ModuleSingleton.instance = new ModuleSingleton());
    }
}

// Singleton with Symbol
const singletonSymbol = Symbol.for("singleton");
const global = globalThis as any;
if (!global[singletonSymbol]) {
    global[singletonSymbol] = new Singleton();
}
const singletonFromSymbol = global[singletonSymbol] as Singleton;
Coding Round
71. Factory pattern

Factory pattern using static methods and interfaces with TypeScript.

  • Factory method: static createUser(type: string, name: string): User
  • Interface: interface User { name: string; getRole(): string; }
  • Abstract factory: GenericFactory<T>
  • Register: register(type: string, creator: () => T): void
typescript
// Factory pattern in TypeScript
interface User {
    name: string;
    getRole(): string;
}

class Admin implements User {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    getRole(): string {
        return "admin";
    }
}

class Guest implements User {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    getRole(): string {
        return "guest";
    }
}

class RegularUser implements User {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    getRole(): string {
        return "regular";
    }
}

class UserFactory {
    static createUser(type: string, name: string): User {
        switch (type) {
            case "admin":
                return new Admin(name);
            case "guest":
                return new Guest(name);
            default:
                return new RegularUser(name);
        }
    }
}

// Usage
const admin = UserFactory.createUser("admin", "Alice");
const guest = UserFactory.createUser("guest", "Bob");

console.log(`${admin.name} role: ${admin.getRole()}`);
console.log(`${guest.name} role: ${guest.getRole()}`);

// Abstract factory
interface Widget {
    draw(): void;
}

class Button implements Widget {
    draw(): void {
        console.log("Drawing Button");
    }
}

class TextField implements Widget {
    draw(): void {
        console.log("Drawing TextField");
    }
}

class WidgetFactory {
    static createWidget(type: string): Widget | null {
        switch (type) {
            case "button":
                return new Button();
            case "textfield":
                return new TextField();
            default:
                return null;
        }
    }
}

const button = WidgetFactory.createWidget("button");
button?.draw();

// Generic factory
class GenericFactory<T> {
    private creators: Map<string, () => T> = new Map();
    
    register(type: string, creator: () => T): void {
        this.creators.set(type, creator);
    }
    
    create(type: string): T | null {
        const creator = this.creators.get(type);
        return creator ? creator() : null;
    }
}

const userFactory = new GenericFactory<User>();
userFactory.register("admin", () => new Admin("Admin"));
userFactory.register("guest", () => new Guest("Guest"));
const adminUser = userFactory.create("admin");
console.log(adminUser?.getRole());
Coding Round
72. Strategy pattern

Strategy pattern using interfaces and composition with TypeScript.

  • Strategy interface: interface PaymentStrategy { pay(amount: number): void; }
  • Context: class PaymentContext
  • Generic: SortStrategy<T>
  • Runtime switching: setStrategy(strategy: PaymentStrategy): void
typescript
// Strategy pattern in TypeScript
interface PaymentStrategy {
    pay(amount: number): void;
}

class CreditCardStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`Paid $${amount} with Credit Card`);
    }
}

class PayPalStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`Paid $${amount} with PayPal`);
    }
}

class CryptoStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`Paid $${amount} with Crypto`);
    }
}

class PaymentContext {
    private strategy: PaymentStrategy;
    
    constructor(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }
    
    setStrategy(strategy: PaymentStrategy): void {
        this.strategy = strategy;
    }
    
    executePayment(amount: number): void {
        this.strategy.pay(amount);
    }
}

// Usage
const context = new PaymentContext(new CreditCardStrategy());
context.executePayment(100);

context.setStrategy(new PayPalStrategy());
context.executePayment(50);

context.setStrategy(new CryptoStrategy());
context.executePayment(75);

// Strategy with type parameter
interface SortStrategy<T> {
    sort(data: T[]): T[];
}

class QuickSort<T> implements SortStrategy<T> {
    sort(data: T[]): T[] {
        return data.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
    }
}

class MergeSort<T> implements SortStrategy<T> {
    sort(data: T[]): T[] {
        return data.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
    }
}

class SortContext<T> {
    private strategy: SortStrategy<T>;
    
    constructor(strategy: SortStrategy<T>) {
        this.strategy = strategy;
    }
    
    setStrategy(strategy: SortStrategy<T>): void {
        this.strategy = strategy;
    }
    
    executeSort(data: T[]): T[] {
        return this.strategy.sort(data);
    }
}
Coding Round
73. Observer pattern

Observer pattern with subject and observers using TypeScript interfaces.

  • Subject: class Subject
  • Observer interface: interface Observer { update(data: string): void; }
  • Attach/Detach: attach(observer: Observer): void
  • Generic: TypedSubject<T>
typescript
// Observer pattern in TypeScript
interface Observer {
    update(data: string): void;
}

class Subject {
    private observers: Observer[] = [];
    private state: string = "";
    
    attach(observer: Observer): void {
        this.observers.push(observer);
    }
    
    detach(observer: Observer): void {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }
    
    setState(state: string): void {
        this.state = state;
        this.notifyObservers();
    }
    
    private notifyObservers(): void {
        for (const observer of this.observers) {
            observer.update(this.state);
        }
    }
}

class ConcreteObserver implements Observer {
    private name: string;
    
    constructor(name: string) {
        this.name = name;
    }
    
    update(data: string): void {
        console.log(`${this.name} received: ${data}`);
    }
}

// Usage
const subject = new Subject();
const observer1 = new ConcreteObserver("Observer1");
const observer2 = new ConcreteObserver("Observer2");

subject.attach(observer1);
subject.attach(observer2);

subject.setState("Hello World");

subject.detach(observer1);
subject.setState("Hello again");

// Observer with type parameter
interface TypedObserver<T> {
    update(data: T): void;
}

class TypedSubject<T> {
    private observers: TypedObserver<T>[] = [];
    private state: T;
    
    constructor(initialState: T) {
        this.state = initialState;
    }
    
    attach(observer: TypedObserver<T>): void {
        this.observers.push(observer);
    }
    
    detach(observer: TypedObserver<T>): void {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }
    
    setState(state: T): void {
        this.state = state;
        this.notifyObservers();
    }
    
    private notifyObservers(): void {
        for (const observer of this.observers) {
            observer.update(this.state);
        }
    }
}

class NumberObserver implements TypedObserver<number> {
    private name: string;
    
    constructor(name: string) {
        this.name = name;
    }
    
    update(data: number): void {
        console.log(`${this.name} received: ${data}`);
    }
}
Coding Round
74. Decorator pattern

Decorator pattern using wrapper classes with TypeScript.

  • Component: interface Coffee
  • Decorator: class MilkDecorator implements Coffee
  • Generic: LoggerDecorator<T>
  • Chaining: Multiple decorators
typescript
// Decorator pattern in TypeScript
interface Coffee {
    cost: number;
    description: string;
}

class SimpleCoffee implements Coffee {
    cost: number = 5.0;
    description: string = "Coffee";
}

class MilkDecorator implements Coffee {
    private coffee: Coffee;
    
    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }
    
    get cost(): number {
        return this.coffee.cost + 2.0;
    }
    
    get description(): string {
        return `${this.coffee.description}, Milk`;
    }
}

class SugarDecorator implements Coffee {
    private coffee: Coffee;
    
    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }
    
    get cost(): number {
        return this.coffee.cost + 1.0;
    }
    
    get description(): string {
        return `${this.coffee.description}, Sugar`;
    }
}

class WhippedCreamDecorator implements Coffee {
    private coffee: Coffee;
    
    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }
    
    get cost(): number {
        return this.coffee.cost + 1.5;
    }
    
    get description(): string {
        return `${this.coffee.description}, Whipped Cream`;
    }
}

// Usage
let coffee: Coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new WhippedCreamDecorator(coffee);

console.log(coffee.description);  // Coffee, Milk, Sugar, Whipped Cream
console.log(coffee.cost);  // 9.5

// Generic decorator
class LoggerDecorator<T> {
    private target: T;
    
    constructor(target: T) {
        this.target = target;
    }
    
    get<T>(key: keyof T): any {
        console.log(`Getting ${String(key)}`);
        return this.target[key];
    }
    
    set<T>(key: keyof T, value: any): void {
        console.log(`Setting ${String(key)} to ${value}`);
        (this.target as any)[key] = value;
    }
}

class User {
    name: string = "";
    age: number = 0;
}

const user = new User();
const loggerUser = new LoggerDecorator(user);
loggerUser.set('name', 'Alice');
console.log(loggerUser.get('name'));
Coding Round
75. Command pattern

Command pattern with execute and undo methods using TypeScript interfaces.

  • Command interface: interface Command { execute(): void; undo(): void; }
  • Command manager: class CommandManager
  • Generic: GenericCommand<T>
  • Undo/Redo: undo(): void
typescript
// Command pattern in TypeScript
interface Command {
    execute(): void;
    undo(): void;
}

class AddCommand implements Command {
    private receiver: number[];
    private value: number;
    
    constructor(receiver: number[], value: number) {
        this.receiver = receiver;
        this.value = value;
    }
    
    execute(): void {
        this.receiver.push(this.value);
    }
    
    undo(): void {
        const index = this.receiver.indexOf(this.value);
        if (index !== -1) {
            this.receiver.splice(index, 1);
        }
    }
}

class CommandManager {
    private history: Command[] = [];
    private redoStack: Command[] = [];
    
    execute(command: Command): void {
        command.execute();
        this.history.push(command);
        this.redoStack = [];
    }
    
    undo(): void {
        const command = this.history.pop();
        if (command) {
            command.undo();
            this.redoStack.push(command);
        }
    }
    
    redo(): void {
        const command = this.redoStack.pop();
        if (command) {
            command.execute();
            this.history.push(command);
        }
    }
}

// Usage
const receiver: number[] = [1, 2, 3];
const manager = new CommandManager();

const addCommand = new AddCommand(receiver, 4);
manager.execute(addCommand);
console.log(receiver);  // [1, 2, 3, 4]

manager.undo();
console.log(receiver);  // [1, 2, 3]

manager.redo();
console.log(receiver);  // [1, 2, 3, 4]

// Generic command
class GenericCommand<T> implements Command {
    private receiver: T;
    private action: (target: T) => void;
    private undoAction: (target: T) => void;
    
    constructor(receiver: T, action: (target: T) => void, undoAction: (target: T) => void) {
        this.receiver = receiver;
        this.action = action;
        this.undoAction = undoAction;
    }
    
    execute(): void {
        this.action(this.receiver);
    }
    
    undo(): void {
        this.undoAction(this.receiver);
    }
}

class Counter {
    value: number = 0;
}

const counter = new Counter();
const incrementCommand = new GenericCommand(
    counter,
    (c) => c.value++,
    (c) => c.value--
);

manager.execute(incrementCommand);
console.log(counter.value);  // 1
manager.undo();
console.log(counter.value);  // 0
Coding Round
76. Memento pattern

Memento pattern for state capture and restoration using TypeScript.

  • Originator: class Originator
  • Memento: interface Memento { getState(): string; }
  • Caretaker: class Caretaker
  • Generic: GenericMemento<T>
typescript
// Memento pattern in TypeScript
interface Memento {
    getState(): string;
}

class ConcreteMemento implements Memento {
    private state: string;
    private date: Date;
    
    constructor(state: string) {
        this.state = state;
        this.date = new Date();
    }
    
    getState(): string {
        return this.state;
    }
    
    getDate(): Date {
        return this.date;
    }
}

class Originator {
    private state: string = "";
    
    setState(state: string): void {
        this.state = state;
        console.log(`State set to: ${state}`);
    }
    
    getState(): string {
        return this.state;
    }
    
    saveState(): Memento {
        return new ConcreteMemento(this.state);
    }
    
    restoreState(memento: Memento): void {
        this.state = memento.getState();
        console.log(`State restored to: ${this.state}`);
    }
}

class Caretaker {
    private mementos: Memento[] = [];
    
    addMemento(memento: Memento): void {
        this.mementos.push(memento);
    }
    
    getMemento(index: number): Memento | null {
        if (index >= 0 && index < this.mementos.length) {
            return this.mementos[index];
        }
        return null;
    }
    
    getHistory(): Memento[] {
        return this.mementos;
    }
}

// Usage
const originator = new Originator();
const caretaker = new Caretaker();

originator.setState("State 1");
caretaker.addMemento(originator.saveState());

originator.setState("State 2");
caretaker.addMemento(originator.saveState());

originator.setState("State 3");

const memento = caretaker.getMemento(0);
if (memento) {
    originator.restoreState(memento);
}

// Generic memento
class GenericMemento<T> {
    private state: T;
    private timestamp: number;
    
    constructor(state: T) {
        this.state = state;
        this.timestamp = Date.now();
    }
    
    getState(): T {
        return this.state;
    }
    
    getTimestamp(): number {
        return this.timestamp;
    }
}

class GenericOriginator<T> {
    private state: T;
    
    constructor(initialState: T) {
        this.state = initialState;
    }
    
    setState(state: T): void {
        this.state = state;
    }
    
    getState(): T {
        return this.state;
    }
    
    saveState(): GenericMemento<T> {
        return new GenericMemento(this.state);
    }
    
    restoreState(memento: GenericMemento<T>): void {
        this.state = memento.getState();
    }
}
Coding Round
77. Mediator pattern

Mediator pattern for centralized communication using TypeScript.

  • Mediator: class Mediator
  • Colleague: interface Colleague { send(message: string): void; receive(message: string): void; }
  • Chat room: class ChatRoom extends Mediator
  • Generic: GenericMediator<T>
typescript
// Mediator pattern in TypeScript
interface Colleague {
    send(message: string): void;
    receive(message: string): void;
}

class Mediator {
    private colleagues: Colleague[] = [];
    
    register(colleague: Colleague): void {
        this.colleagues.push(colleague);
    }
    
    send(message: string, sender: Colleague): void {
        for (const colleague of this.colleagues) {
            if (colleague !== sender) {
                colleague.receive(message);
            }
        }
    }
}

class ConcreteColleague implements Colleague {
    private name: string;
    private mediator: Mediator;
    
    constructor(name: string, mediator: Mediator) {
        this.name = name;
        this.mediator = mediator;
        this.mediator.register(this);
    }
    
    send(message: string): void {
        console.log(`${this.name} sends: ${message}`);
        this.mediator.send(message, this);
    }
    
    receive(message: string): void {
        console.log(`${this.name} received: ${message}`);
    }
}

// Usage
const mediator = new Mediator();
const alice = new ConcreteColleague("Alice", mediator);
const bob = new ConcreteColleague("Bob", mediator);

alice.send("Hello Bob!");

// Chat room mediator
class ChatRoom extends Mediator {
    private history: string[] = [];
    
    send(message: string, sender: Colleague): void {
        this.history.push(`${(sender as ConcreteColleague).name}: ${message}`);
        super.send(message, sender);
    }
    
    getHistory(): string[] {
        return this.history;
    }
}

const chatRoom = new ChatRoom();
const user1 = new ConcreteColleague("User1", chatRoom);
const user2 = new ConcreteColleague("User2", chatRoom);

user1.send("Hello everyone!");
console.log(chatRoom.getHistory());

// Generic mediator
class GenericMediator<T> {
    private colleagues: Map<string, T> = new Map();
    private messageHandlers: Map<string, (message: any) => void> = new Map();
    
    register(id: string, colleague: T): void {
        this.colleagues.set(id, colleague);
    }
    
    send(id: string, message: any): void {
        const handler = this.messageHandlers.get(id);
        if (handler) {
            handler(message);
        }
    }
    
    onMessage(id: string, handler: (message: any) => void): void {
        this.messageHandlers.set(id, handler);
    }
}
Coding Round
78. Chain of Responsibility

Chain of Responsibility using abstract handlers with TypeScript.

  • Handler: interface Handler
  • AbstractHandler: abstract class AbstractHandler implements Handler
  • Chain: setNext(handler: Handler): Handler
  • Generic: class Chain<T>
typescript
// Chain of Responsibility in TypeScript
interface Handler {
    setNext(handler: Handler): Handler;
    handle(request: string): string | null;
}

abstract class AbstractHandler implements Handler {
    private nextHandler: Handler | null = null;
    
    setNext(handler: Handler): Handler {
        this.nextHandler = handler;
        return handler;
    }
    
    handle(request: string): string | null {
        if (this.nextHandler) {
            return this.nextHandler.handle(request);
        }
        return null;
    }
}

class AuthHandler extends AbstractHandler {
    handle(request: string): string | null {
        if (request.includes("token")) {
            console.log("Authentication passed");
            return super.handle(request);
        }
        console.log("Authentication failed");
        return null;
    }
}

class LoggerHandler extends AbstractHandler {
    handle(request: string): string | null {
        console.log(`Logging request: ${request}`);
        return super.handle(request);
    }
}

class PermissionHandler extends AbstractHandler {
    handle(request: string): string | null {
        if (request.includes("read")) {
            console.log("Permission granted");
            return super.handle(request);
        }
        console.log("Permission denied");
        return null;
    }
}

// Usage
const auth = new AuthHandler();
const logger = new LoggerHandler();
const permission = new PermissionHandler();

auth.setNext(logger).setNext(permission);
auth.handle("token:valid, read:true");

// Generic chain
class Chain<T> {
    private handlers: ((request: T) => T | null)[] = [];
    
    addHandler(handler: (request: T) => T | null): void {
        this.handlers.push(handler);
    }
    
    process(request: T): T | null {
        let result: T | null = request;
        for (const handler of this.handlers) {
            result = handler(result);
            if (result === null) {
                break;
            }
        }
        return result;
    }
}

const chain = new Chain<string>();
chain.addHandler((req) => req.includes("token") ? req : null);
chain.addHandler((req) => req.includes("read") ? req : null);
chain.addHandler((req) => console.log(`Processing: ${req}`) || req);

const result = chain.process("token:valid, read:true");
console.log(result);
Coding Round
79. State pattern

State pattern for changing behavior with state using TypeScript interfaces.

  • State: interface State { handle(context: Context): void; }
  • Context: class Context
  • Transitions: setState(state: State): void
  • State with transitions: StateWithTransition
typescript
// State pattern in TypeScript
interface State {
    handle(context: Context): void;
}

class ReadyState implements State {
    handle(context: Context): void {
        console.log("Ready: Waiting for input");
        context.setState(new ProcessingState());
    }
}

class ProcessingState implements State {
    handle(context: Context): void {
        console.log("Processing: Working on task");
        context.setState(new CompletedState());
    }
}

class CompletedState implements State {
    handle(context: Context): void {
        console.log("Completed: Task finished");
    }
}

class Context {
    private state: State;
    
    constructor() {
        this.state = new ReadyState();
    }
    
    setState(state: State): void {
        this.state = state;
    }
    
    request(): void {
        this.state.handle(this);
    }
}

// Usage
const context = new Context();
context.request();  // Ready: Waiting for input
context.request();  // Processing: Working on task
context.request();  // Completed: Task finished

// State with transitions
interface StateWithTransition {
    handle(context: StateContext): void;
    getTransitions(): string[];
}

class StateContext {
    private currentState: StateWithTransition;
    private history: string[] = [];
    
    constructor(initialState: StateWithTransition) {
        this.currentState = initialState;
    }
    
    setState(state: StateWithTransition): void {
        this.currentState = state;
    }
    
    request(): void {
        this.currentState.handle(this);
    }
    
    getHistory(): string[] {
        return this.history;
    }
}

class PendingState implements StateWithTransition {
    handle(context: StateContext): void {
        console.log("Pending: Waiting for approval");
        context.setState(new ProcessingState2());
    }
    
    getTransitions(): string[] {
        return ["processing", "cancelled"];
    }
}

class ProcessingState2 implements StateWithTransition {
    handle(context: StateContext): void {
        console.log("Processing: Working on task");
        context.setState(new CompletedState2());
    }
    
    getTransitions(): string[] {
        return ["completed", "failed"];
    }
}

class CompletedState2 implements StateWithTransition {
    handle(context: StateContext): void {
        console.log("Completed: Task finished");
    }
    
    getTransitions(): string[] {
        return [];
    }
}
Coding Round
80. Proxy pattern

Proxy pattern for controlling access using TypeScript.

  • Subject: interface Subject { request(): string; }
  • Proxy: class Proxy implements Subject
  • Virtual proxy: class VirtualProxy implements Subject
  • Protection proxy: class ProtectionProxy implements Subject
typescript
// Proxy pattern in TypeScript
interface Subject {
    request(): string;
}

class RealSubject implements Subject {
    request(): string {
        return "RealSubject: Handling request";
    }
}

class Proxy implements Subject {
    private realSubject: RealSubject | null = null;
    
    request(): string {
        if (this.checkAccess()) {
            if (!this.realSubject) {
                this.realSubject = new RealSubject();
            }
            const result = this.realSubject.request();
            this.logAccess();
            return result;
        }
        return "Proxy: Access denied";
    }
    
    private checkAccess(): boolean {
        console.log("Proxy: Checking access");
        return true;
    }
    
    private logAccess(): void {
        console.log("Proxy: Logging access");
    }
}

// Usage
const proxy = new Proxy();
console.log(proxy.request());

// Virtual proxy (lazy loading)
class VirtualProxy implements Subject {
    private realSubject: RealSubject | null = null;
    
    request(): string {
        if (!this.realSubject) {
            console.log("Proxy: Creating real subject");
            this.realSubject = new RealSubject();
        }
        return this.realSubject.request();
    }
}

const virtualProxy = new VirtualProxy();
console.log(virtualProxy.request());
console.log(virtualProxy.request());

// Protection proxy
class ProtectionProxy implements Subject {
    private realSubject: RealSubject | null = null;
    private user: string;
    
    constructor(user: string) {
        this.user = user;
    }
    
    request(): string {
        if (this.user === "admin") {
            if (!this.realSubject) {
                this.realSubject = new RealSubject();
            }
            return this.realSubject.request();
        }
        return `Proxy: Access denied for user ${this.user}`;
    }
}

const adminProxy = new ProtectionProxy("admin");
const guestProxy = new ProtectionProxy("guest");
console.log(adminProxy.request());
console.log(guestProxy.request());

// Generic proxy
class GenericProxy<T> {
    private target: T;
    private interceptors: ((key: keyof T, ...args: any[]) => any)[] = [];
    
    constructor(target: T) {
        this.target = target;
    }
    
    addInterceptor(interceptor: (key: keyof T, ...args: any[]) => any): void {
        this.interceptors.push(interceptor);
    }
    
    getProxy(): T {
        return new Proxy(this.target, {
            get: (target, key: keyof T, receiver) => {
                let result = target[key];
                for (const interceptor of this.interceptors) {
                    result = interceptor(key, ...result);
                }
                return result;
            }
        });
    }
}
Coding Round
81. Flyweight pattern

Flyweight pattern for sharing objects using TypeScript.

  • Flyweight: interface Flyweight { operation(uniqueState: string): void; }
  • Factory: class FlyweightFactory
  • Character flyweight: class CharacterFactory
  • Generic: GenericFlyweight<T>
typescript
// Flyweight pattern in TypeScript
interface Flyweight {
    operation(uniqueState: string): void;
}

class ConcreteFlyweight implements Flyweight {
    private sharedState: string;
    
    constructor(sharedState: string) {
        this.sharedState = sharedState;
    }
    
    operation(uniqueState: string): void {
        console.log(`Shared: ${this.sharedState}, Unique: ${uniqueState}`);
    }
}

class FlyweightFactory {
    private flyweights: Map<string, Flyweight> = new Map();
    
    getFlyweight(sharedState: string): Flyweight {
        if (!this.flyweights.has(sharedState)) {
            this.flyweights.set(sharedState, new ConcreteFlyweight(sharedState));
            console.log(`Creating new flyweight for: ${sharedState}`);
        }
        return this.flyweights.get(sharedState)!;
    }
    
    getCount(): number {
        return this.flyweights.size;
    }
}

// Usage
const factory = new FlyweightFactory();
const fw1 = factory.getFlyweight("state1");
const fw2 = factory.getFlyweight("state1");
const fw3 = factory.getFlyweight("state2");

fw1.operation("unique1");
fw2.operation("unique2");
fw3.operation("unique3");

console.log(`Flyweight count: ${factory.getCount()}`);

// Character flyweight
class Character {
    private char: string;
    
    constructor(char: string) {
        this.char = char;
    }
    
    display(fontSize: number): void {
        console.log(`Character: ${this.char}, Size: ${fontSize}`);
    }
}

class CharacterFactory {
    private characters: Map<string, Character> = new Map();
    
    getCharacter(char: string): Character {
        if (!this.characters.has(char)) {
            this.characters.set(char, new Character(char));
        }
        return this.characters.get(char)!;
    }
}

const charFactory = new CharacterFactory();
const text = "hello";
for (const char of text) {
    const character = charFactory.getCharacter(char);
    character.display(12);
}

// Generic flyweight
class GenericFlyweight<T> {
    private instances: Map<string, T> = new Map();
    private creator: (key: string) => T;
    
    constructor(creator: (key: string) => T) {
        this.creator = creator;
    }
    
    get(key: string): T {
        if (!this.instances.has(key)) {
            this.instances.set(key, this.creator(key));
        }
        return this.instances.get(key)!;
    }
    
    getCount(): number {
        return this.instances.size;
    }
}
Coding Round
82. Bridge pattern

Bridge pattern for separating abstraction from implementation using TypeScript.

  • Implementation: interface Implementation
  • Abstraction: class Abstraction
  • Extended: class ExtendedAbstraction extends Abstraction
  • Generic: GenericImplementation<T>
typescript
// Bridge pattern in TypeScript
interface Implementation {
    operationImpl(): string;
}

class ConcreteImplementationA implements Implementation {
    operationImpl(): string {
        return "ConcreteImplementationA: Operation";
    }
}

class ConcreteImplementationB implements Implementation {
    operationImpl(): string {
        return "ConcreteImplementationB: Operation";
    }
}

class Abstraction {
    protected implementation: Implementation;
    
    constructor(implementation: Implementation) {
        this.implementation = implementation;
    }
    
    operation(): string {
        return `Abstraction: Additional logic - ${this.implementation.operationImpl()}`;
    }
}

class ExtendedAbstraction extends Abstraction {
    operation(): string {
        return `ExtendedAbstraction: More logic - ${this.implementation.operationImpl()}`;
    }
}

// Usage
const implA = new ConcreteImplementationA();
const implB = new ConcreteImplementationB();
const abstraction1 = new Abstraction(implA);
const abstraction2 = new Abstraction(implB);

console.log(abstraction1.operation());
console.log(abstraction2.operation());

const extended = new ExtendedAbstraction(implA);
console.log(extended.operation());

// Generic bridge
interface GenericImplementation<T> {
    process(data: T): T;
}

class StringImplementation implements GenericImplementation<string> {
    process(data: string): string {
        return data.toUpperCase();
    }
}

class NumberImplementation implements GenericImplementation<number> {
    process(data: number): number {
        return data * 2;
    }
}

class GenericAbstraction<T> {
    protected implementation: GenericImplementation<T>;
    
    constructor(implementation: GenericImplementation<T>) {
        this.implementation = implementation;
    }
    
    process(data: T): T {
        return this.implementation.process(data);
    }
}

const stringImpl = new StringImplementation();
const numberImpl = new NumberImplementation();
const stringAbstraction = new GenericAbstraction(stringImpl);
const numberAbstraction = new GenericAbstraction(numberImpl);

console.log(stringAbstraction.process("hello"));  // HELLO
console.log(numberAbstraction.process(21));  // 42
Coding Round
83. Adapter pattern

Adapter pattern for converting interfaces using TypeScript.

  • Target: interface Target { request(): string; }
  • Adaptee: class Adaptee
  • Adapter: class Adapter implements Target
  • Generic: GenericAdapter<T>
typescript
// Adapter pattern in TypeScript
interface Target {
    request(): string;
}

class Adaptee {
    specificRequest(): string {
        return "Adaptee: Specific Request";
    }
}

class Adapter implements Target {
    private adaptee: Adaptee;
    
    constructor(adaptee: Adaptee) {
        this.adaptee = adaptee;
    }
    
    request(): string {
        return this.adaptee.specificRequest();
    }
}

// Usage
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request());

// Class adapter (multiple inheritance simulation)
class ClassAdapter extends Adaptee implements Target {
    request(): string {
        return this.specificRequest();
    }
}

const classAdapter = new ClassAdapter();
console.log(classAdapter.request());

// Object adapter with type conversion
interface ModernInterface {
    getData(): string;
}

class LegacySystem {
    getLegacyData(): string {
        return "Legacy data";
    }
}

class LegacyToModernAdapter implements ModernInterface {
    private legacy: LegacySystem;
    
    constructor(legacy: LegacySystem) {
        this.legacy = legacy;
    }
    
    getData(): string {
        const data = this.legacy.getLegacyData();
        return `Modern: ${data}`;
    }
}

const legacy = new LegacySystem();
const modern = new LegacyToModernAdapter(legacy);
console.log(modern.getData());

// Generic adapter
interface Target<T> {
    request(data: T): T;
}

class GenericAdaptee<T> {
    specificRequest(data: T): T {
        return data;
    }
}

class GenericAdapter<T> implements Target<T> {
    private adaptee: GenericAdaptee<T>;
    
    constructor(adaptee: GenericAdaptee<T>) {
        this.adaptee = adaptee;
    }
    
    request(data: T): T {
        return this.adaptee.specificRequest(data);
    }
}

const genericAdaptee = new GenericAdaptee<string>();
const genericAdapter = new GenericAdapter(genericAdaptee);
console.log(genericAdapter.request("Hello"));
Coding Round
84. Facade pattern

Facade pattern for simplifying subsystems using TypeScript.

  • Subsystems: class SubsystemA, SubsystemB, SubsystemC
  • Facade: class Facade
  • Simplified operation: operation(): string
  • Generic: DatabaseFacade
typescript
// Facade pattern in TypeScript
class SubsystemA {
    operationA(): string {
        return "SubsystemA: Operation";
    }
}

class SubsystemB {
    operationB(): string {
        return "SubsystemB: Operation";
    }
}

class SubsystemC {
    operationC(): string {
        return "SubsystemC: Operation";
    }
}

class Facade {
    private subsystemA: SubsystemA;
    private subsystemB: SubsystemB;
    private subsystemC: SubsystemC;
    
    constructor() {
        this.subsystemA = new SubsystemA();
        this.subsystemB = new SubsystemB();
        this.subsystemC = new SubsystemC();
    }
    
    operation(): string {
        const results = [
            this.subsystemA.operationA(),
            this.subsystemB.operationB(),
            this.subsystemC.operationC()
        ];
        return `Facade: Complex operation - ${results.join(", ")}`;
    }
    
    simplifiedOperation(): string {
        return `Facade: Simplified operation - ${this.subsystemA.operationA()}`;
    }
}

// Usage
const facade = new Facade();
console.log(facade.operation());
console.log(facade.simplifiedOperation());

// Generic facade
interface Database {
    connect(): string;
    query(sql: string): string;
    disconnect(): string;
}

class MySQLDatabase implements Database {
    connect(): string {
        return "Connected to MySQL";
    }
    
    query(sql: string): string {
        return `MySQL executing: ${sql}`;
    }
    
    disconnect(): string {
        return "Disconnected from MySQL";
    }
}

class PostgreSQLDatabase implements Database {
    connect(): string {
        return "Connected to PostgreSQL";
    }
    
    query(sql: string): string {
        return `PostgreSQL executing: ${sql}`;
    }
    
    disconnect(): string {
        return "Disconnected from PostgreSQL";
    }
}

class DatabaseFacade {
    private db: Database;
    
    constructor(db: Database) {
        this.db = db;
    }
    
    executeQuery(sql: string): string {
        const connect = this.db.connect();
        const result = this.db.query(sql);
        const disconnect = this.db.disconnect();
        return `${connect}
${result}
${disconnect}`;
    }
}

const mysql = new MySQLDatabase();
const postgres = new PostgreSQLDatabase();
const mysqlFacade = new DatabaseFacade(mysql);
const postgresFacade = new DatabaseFacade(postgres);

console.log(mysqlFacade.executeQuery("SELECT * FROM users"));
console.log(postgresFacade.executeQuery("SELECT * FROM users"));
Coding Round
85. Composite pattern

Composite pattern for tree structures using TypeScript.

  • Component: interface Component { operation(): string; }
  • Leaf: class Leaf implements Component
  • Composite: class Composite implements Component
  • Generic: GenericComposite<T>
typescript
// Composite pattern in TypeScript
interface Component {
    operation(): string;
}

class Leaf implements Component {
    private name: string;
    
    constructor(name: string) {
        this.name = name;
    }
    
    operation(): string {
        return `Leaf ${this.name}: Operation`;
    }
}

class Composite implements Component {
    private name: string;
    private children: Component[] = [];
    
    constructor(name: string) {
        this.name = name;
    }
    
    add(component: Component): void {
        this.children.push(component);
    }
    
    remove(component: Component): void {
        const index = this.children.indexOf(component);
        if (index !== -1) {
            this.children.splice(index, 1);
        }
    }
    
    operation(): string {
        const results = this.children.map(child => child.operation());
        return `Composite ${this.name}: [${results.join(", ")}]`;
    }
}

// Usage
const leaf1 = new Leaf("A");
const leaf2 = new Leaf("B");
const leaf3 = new Leaf("C");

const composite1 = new Composite("Composite1");
composite1.add(leaf1);
composite1.add(leaf2);

const composite2 = new Composite("Root");
composite2.add(composite1);
composite2.add(leaf3);

console.log(composite2.operation());

// Generic composite
interface GenericComponent<T> {
    operation(): T;
}

class GenericLeaf<T> implements GenericComponent<T> {
    private data: T;
    
    constructor(data: T) {
        this.data = data;
    }
    
    operation(): T {
        return this.data;
    }
}

class GenericComposite<T> implements GenericComponent<T> {
    private children: GenericComponent<T>[] = [];
    private combine: (results: T[]) => T;
    
    constructor(combine: (results: T[]) => T) {
        this.combine = combine;
    }
    
    add(component: GenericComponent<T>): void {
        this.children.push(component);
    }
    
    operation(): T {
        const results = this.children.map(child => child.operation());
        return this.combine(results);
    }
}

const sumComposite = new GenericComposite<number>((results) => 
    results.reduce((a, b) => a + b, 0)
);
sumComposite.add(new GenericLeaf(1));
sumComposite.add(new GenericLeaf(2));
sumComposite.add(new GenericLeaf(3));

console.log(sumComposite.operation()); // 6
Coding Round
86. Visitor pattern

Visitor pattern for adding operations without modifying elements using TypeScript.

  • Visitor: interface Visitor
  • Element: interface Element { accept(visitor: Visitor): string; }
  • Concrete visitor: class ConcreteVisitor implements Visitor
  • Generic: TypedVisitor<T>
typescript
// Visitor pattern in TypeScript
interface Visitor {
    visitElementA(element: ElementA): string;
    visitElementB(element: ElementB): string;
}

interface Element {
    accept(visitor: Visitor): string;
}

class ElementA implements Element {
    private data: string;
    
    constructor(data: string) {
        this.data = data;
    }
    
    accept(visitor: Visitor): string {
        return visitor.visitElementA(this);
    }
    
    getData(): string {
        return this.data;
    }
}

class ElementB implements Element {
    private data: string;
    
    constructor(data: string) {
        this.data = data;
    }
    
    accept(visitor: Visitor): string {
        return visitor.visitElementB(this);
    }
    
    getData(): string {
        return this.data;
    }
}

class ConcreteVisitor implements Visitor {
    visitElementA(element: ElementA): string {
        return `Visiting ElementA with data: ${element.getData()}`;
    }
    
    visitElementB(element: ElementB): string {
        return `Visiting ElementB with data: ${element.getData()}`;
    }
}

// Usage
const visitor = new ConcreteVisitor();
const elementA = new ElementA("A data");
const elementB = new ElementB("B data");

console.log(elementA.accept(visitor));
console.log(elementB.accept(visitor));

// Visitor with type parameter
interface TypedVisitor<T> {
    visitElementA(element: ElementA): T;
    visitElementB(element: ElementB): T;
}

class StringVisitor implements TypedVisitor<string> {
    visitElementA(element: ElementA): string {
        return `StringVisitor: ElementA - ${element.getData()}`;
    }
    
    visitElementB(element: ElementB): string {
        return `StringVisitor: ElementB - ${element.getData()}`;
    }
}

class NumberVisitor implements TypedVisitor<number> {
    visitElementA(element: ElementA): number {
        return element.getData().length;
    }
    
    visitElementB(element: ElementB): number {
        return element.getData().length;
    }
}

const stringVisitor = new StringVisitor();
const numberVisitor = new NumberVisitor();

console.log(elementA.accept(stringVisitor));
console.log(elementA.accept(numberVisitor));
console.log(elementB.accept(stringVisitor));
console.log(elementB.accept(numberVisitor));
Coding Round
87. Iterator pattern

Iterator pattern for sequential access using TypeScript generics.

  • Iterator: interface Iterator<T> { next(): T | null; hasNext(): boolean; reset(): void; }
  • Collection: class CustomCollection<T>
  • Fibonacci iterator: class FibonacciIterator implements Iterator<number>
  • Step iterator: class StepIterator<T> implements Iterator<T>
typescript
// Iterator pattern in TypeScript
interface Iterator<T> {
    next(): T | null;
    hasNext(): boolean;
    reset(): void;
}

class ArrayIterator<T> implements Iterator<T> {
    private collection: T[];
    private index: number = 0;
    
    constructor(collection: T[]) {
        this.collection = collection;
    }
    
    next(): T | null {
        if (this.hasNext()) {
            return this.collection[this.index++];
        }
        return null;
    }
    
    hasNext(): boolean {
        return this.index < this.collection.length;
    }
    
    reset(): void {
        this.index = 0;
    }
}

class CustomCollection<T> {
    private items: T[] = [];
    
    add(item: T): void {
        this.items.push(item);
    }
    
    getIterator(): Iterator<T> {
        return new ArrayIterator(this.items);
    }
}

// Usage
const collection = new CustomCollection<string>();
collection.add("A");
collection.add("B");
collection.add("C");

const iterator = collection.getIterator();
while (iterator.hasNext()) {
    console.log(iterator.next());
}

// Fibonacci iterator
class FibonacciIterator implements Iterator<number> {
    private current: number = 0;
    private next: number = 1;
    private limit: number;
    private count: number = 0;
    
    constructor(limit: number) {
        this.limit = limit;
    }
    
    next(): number | null {
        if (this.count >= this.limit) {
            return null;
        }
        const value = this.current;
        [this.current, this.next] = [this.next, this.current + this.next];
        this.count++;
        return value;
    }
    
    hasNext(): boolean {
        return this.count < this.limit;
    }
    
    reset(): void {
        this.current = 0;
        this.next = 1;
        this.count = 0;
    }
}

const fibIterator = new FibonacciIterator(10);
while (fibIterator.hasNext()) {
    console.log(fibIterator.next());
}

// Generic iterator with step
class StepIterator<T> implements Iterator<T> {
    private collection: T[];
    private index: number = 0;
    private step: number;
    
    constructor(collection: T[], step: number) {
        this.collection = collection;
        this.step = step;
    }
    
    next(): T | null {
        if (this.hasNext()) {
            const value = this.collection[this.index];
            this.index += this.step;
            return value;
        }
        return null;
    }
    
    hasNext(): boolean {
        return this.index < this.collection.length;
    }
    
    reset(): void {
        this.index = 0;
    }
}
Coding Round
88. Template Method pattern

Template Method for algorithm skeletons using TypeScript abstract classes.

  • AbstractClass: abstract class AbstractClass
  • Template method: templateMethod(): string
  • ConcreteClass: class ConcreteClass extends AbstractClass
  • Generic: GenericTemplate<T, U>
typescript
// Template Method pattern in TypeScript
abstract class AbstractClass {
    templateMethod(): string {
        const results = [
            this.step1(),
            this.step2(),
            this.step3()
        ];
        return results.join(" -> ");
    }
    
    step1(): string {
        return "Step 1";
    }
    
    abstract step2(): string;
    
    step3(): string {
        return "Step 3";
    }
}

class ConcreteClass extends AbstractClass {
    step2(): string {
        return "Concrete Step 2";
    }
}

// Usage
const concrete = new ConcreteClass();
console.log(concrete.templateMethod());

// Template method with hooks
abstract class DataProcessor {
    process(data: any): any {
        if (this.beforeProcess(data)) {
            const result = this.transform(data);
            this.afterProcess(result);
            return result;
        }
        return null;
    }
    
    protected beforeProcess(data: any): boolean {
        return true;
    }
    
    protected abstract transform(data: any): any;
    
    protected afterProcess(data: any): void {
        // Optional hook
    }
}

class JSONProcessor extends DataProcessor {
    protected transform(data: any): any {
        return JSON.parse(data);
    }
    
    protected beforeProcess(data: any): boolean {
        return typeof data === "string";
    }
    
    protected afterProcess(data: any): void {
        console.log("JSON processed successfully");
    }
}

class XMLProcessor extends DataProcessor {
    protected transform(data: any): any {
        // Simulate XML parsing
        return { parsed: data };
    }
}

const jsonProcessor = new JSONProcessor();
const xmlProcessor = new XMLProcessor();

console.log(jsonProcessor.process('{"name":"Alice"}'));
console.log(xmlProcessor.process("<user>Bob</user>"));

// Generic template method
abstract class GenericTemplate<T, U> {
    templateMethod(input: T): U {
        const validated = this.validate(input);
        const processed = this.process(validated);
        return this.format(processed);
    }
    
    protected abstract validate(input: T): T;
    protected abstract process(input: T): U;
    protected abstract format(input: U): U;
}

class StringTemplate extends GenericTemplate<string, string> {
    protected validate(input: string): string {
        return input.trim();
    }
    
    protected process(input: string): string {
        return input.toUpperCase();
    }
    
    protected format(input: string): string {
        return `[${input}]`;
    }
}
Coding Round
89. Builder pattern

Builder pattern for constructing complex objects using TypeScript.

  • Builder: interface Builder
  • Director: class Director
  • Generic builder: class GenericBuilder<T>
  • Fluent interface: Method chaining
typescript
// Builder pattern in TypeScript
class Product {
    private parts: string[] = [];
    
    add(part: string): void {
        this.parts.push(part);
    }
    
    listParts(): string {
        return this.parts.join(", ");
    }
}

interface Builder {
    reset(): void;
    buildStepA(): void;
    buildStepB(): void;
    getResult(): Product;
}

class ConcreteBuilder implements Builder {
    private product: Product;
    
    constructor() {
        this.product = new Product();
    }
    
    reset(): void {
        this.product = new Product();
    }
    
    buildStepA(): void {
        this.product.add("Part A");
    }
    
    buildStepB(): void {
        this.product.add("Part B");
    }
    
    getResult(): Product {
        const result = this.product;
        this.reset();
        return result;
    }
}

class Director {
    private builder: Builder;
    
    constructor(builder: Builder) {
        this.builder = builder;
    }
    
    buildMinimal(): void {
        this.builder.buildStepA();
    }
    
    buildFull(): void {
        this.builder.buildStepA();
        this.builder.buildStepB();
    }
}

// Usage
const builder = new ConcreteBuilder();
const director = new Director(builder);

director.buildMinimal();
const product1 = builder.getResult();
console.log(product1.listParts()); // Part A

director.buildFull();
const product2 = builder.getResult();
console.log(product2.listParts()); // Part A, Part B

// Generic builder
class GenericBuilder<T> {
    private target: Partial<T> = {};
    
    set<K extends keyof T>(key: K, value: T[K]): this {
        this.target[key] = value;
        return this;
    }
    
    build(): T {
        return this.target as T;
    }
}

interface User {
    name: string;
    age: number;
    email: string;
}

const userBuilder = new GenericBuilder<User>();
const user = userBuilder
    .set("name", "Alice")
    .set("age", 25)
    .set("email", "alice@example.com")
    .build();

console.log(user);

// Fluent builder with validation
class UserBuilder {
    private user: Partial<User> = {};
    
    name(name: string): this {
        if (name.length < 2) {
            throw new Error("Name must be at least 2 characters");
        }
        this.user.name = name;
        return this;
    }
    
    age(age: number): this {
        if (age < 0 || age > 150) {
            throw new Error("Invalid age");
        }
        this.user.age = age;
        return this;
    }
    
    email(email: string): this {
        if (!email.includes("@")) {
            throw new Error("Invalid email");
        }
        this.user.email = email;
        return this;
    }
    
    build(): User {
        if (!this.user.name || !this.user.email) {
            throw new Error("Name and email are required");
        }
        return this.user as User;
    }
}
Coding Round
90. Prototype pattern

Prototype pattern for cloning objects using TypeScript.

  • Prototype: interface Prototype { clone(): Prototype; deepClone(): Prototype; }
  • ConcretePrototype: class ConcretePrototype implements Prototype
  • Generic: GenericPrototype<T>
  • Registry: class PrototypeRegistry
typescript
// Prototype pattern in TypeScript
interface Prototype {
    clone(): Prototype;
    deepClone(): Prototype;
}

class ConcretePrototype implements Prototype {
    private name: string;
    private nested: { value: number };
    
    constructor(name: string, nested: { value: number }) {
        this.name = name;
        this.nested = nested;
    }
    
    clone(): Prototype {
        return new ConcretePrototype(this.name, this.nested);
    }
    
    deepClone(): Prototype {
        return new ConcretePrototype(
            this.name,
            { value: this.nested.value }
        );
    }
    
    getName(): string {
        return this.name;
    }
    
    getNestedValue(): number {
        return this.nested.value;
    }
    
    setName(name: string): void {
        this.name = name;
    }
    
    setNestedValue(value: number): void {
        this.nested.value = value;
    }
}

// Usage
const original = new ConcretePrototype("Original", { value: 42 });
const copy = original.clone() as ConcretePrototype;
copy.setName("Copy");
copy.setNestedValue(99);

console.log(original.getName()); // Original
console.log(original.getNestedValue()); // 42 (shallow copy)

const deepCopy = original.deepClone() as ConcretePrototype;
deepCopy.setNestedValue(100);
console.log(original.getNestedValue()); // 42 (deep copy)

// Generic prototype
class GenericPrototype<T> implements Prototype {
    constructor(public data: T) {}
    
    clone(): Prototype {
        return new GenericPrototype(this.data);
    }
    
    deepClone(): Prototype {
        return new GenericPrototype(JSON.parse(JSON.stringify(this.data)));
    }
    
    getData(): T {
        return this.data;
    }
    
    setData(data: T): void {
        this.data = data;
    }
}

const genOriginal = new GenericPrototype({ name: "Alice", age: 25 });
const genCopy = genOriginal.clone() as GenericPrototype<{ name: string; age: number }>;
genCopy.setData({ name: "Bob", age: 30 });

console.log(genOriginal.getData()); // { name: "Alice", age: 25 }
console.log(genCopy.getData()); // { name: "Bob", age: 30 }

// Prototype registry
class PrototypeRegistry {
    private prototypes: Map<string, Prototype> = new Map();
    
    register(name: string, prototype: Prototype): void {
        this.prototypes.set(name, prototype);
    }
    
    get(name: string): Prototype | null {
        const prototype = this.prototypes.get(name);
        return prototype ? prototype.clone() : null;
    }
}

const registry = new PrototypeRegistry();
registry.register("user", new ConcretePrototype("User", { value: 1 }));
const userPrototype = registry.get("user");
console.log(userPrototype);
Coding Round
91. Error Handling in TypeScript

Error handling using custom error classes, Result type, and Either pattern.

  • Custom error: class AppError extends Error
  • Result type: type Result<T, E = Error>
  • Either pattern: type Either<L, R>
  • Async result: safeAsync<T>(fn: () => Promise<T>): Promise<Result<T>>
typescript
// Error Handling in TypeScript
// Custom error class
class AppError extends Error {
    constructor(
        public message: string,
        public code: number,
        public status: number = 400
    ) {
        super(message);
        this.name = "AppError";
    }
}

// Result type
type Result<T, E = Error> =
    | { success: true; data: T }
    | { success: false; error: E };

function safeOperation<T>(fn: () => T): Result<T> {
    try {
        return { success: true, data: fn() };
    } catch (error) {
        return { success: false, error: error as Error };
    }
}

// Usage
const result = safeOperation(() => {
    if (Math.random() > 0.5) {
        throw new Error("Random error");
    }
    return 42;
});

if (result.success) {
    console.log("Data:", result.data);
} else {
    console.log("Error:", result.error.message);
}

// Async result
async function safeAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {
    try {
        const data = await fn();
        return { success: true, data };
    } catch (error) {
        return { success: false, error: error as Error };
    }
}

// Either type
type Either<L, R> =
    | { kind: "left"; left: L }
    | { kind: "right"; right: R };

function divide(a: number, b: number): Either<string, number> {
    if (b === 0) {
        return { kind: "left", left: "Division by zero" };
    }
    return { kind: "right", right: a / b };
}

const divisionResult = divide(10, 2);
if (divisionResult.kind === "right") {
    console.log("Result:", divisionResult.right);
} else {
    console.log("Error:", divisionResult.left);
}

// Try-catch with specific error types
function processUser(data: any): void {
    try {
        if (!data.name) {
            throw new AppError("Name is required", 1001, 400);
        }
        if (!data.email) {
            throw new AppError("Email is required", 1002, 400);
        }
        console.log("Processing user:", data);
    } catch (error) {
        if (error instanceof AppError) {
            console.log(`App error [${error.code}]: ${error.message}`);
        } else {
            console.log("Unexpected error:", error);
        }
    }
}

processUser({ name: "Alice" });
Coding Round
92. Serialization in TypeScript

Serialization and deserialization using JSON with TypeScript interfaces.

  • Serializable: interface Serializable { toJSON(): any; fromJSON(data: any): void; }
  • Serializer: class Serializer
  • Class method: static fromJSON(data: any): User
  • Validation: ValidatedSerializer
typescript
// Serialization and Deserialization in TypeScript
interface Serializable {
    toJSON(): any;
    fromJSON(data: any): void;
}

class User implements Serializable {
    constructor(
        public id: number,
        public name: string,
        public email: string,
        public createdAt: Date = new Date()
    ) {}
    
    toJSON(): any {
        return {
            id: this.id,
            name: this.name,
            email: this.email,
            createdAt: this.createdAt.toISOString()
        };
    }
    
    fromJSON(data: any): void {
        this.id = data.id;
        this.name = data.name;
        this.email = data.email;
        this.createdAt = new Date(data.createdAt);
    }
    
    static fromJSON(data: any): User {
        const user = new User(data.id, data.name, data.email);
        user.fromJSON(data);
        return user;
    }
}

// Serialization functions
function serialize<T>(obj: T): string {
    return JSON.stringify(obj);
}

function deserialize<T>(json: string): T {
    return JSON.parse(json);
}

// Usage
const user = new User(1, "Alice", "alice@example.com");
const serialized = serialize(user);
console.log(serialized);

const deserialized = deserialize<User>(serialized);
console.log(deserialized);

// Custom serialization with class
class Serializer {
    static serialize<T>(obj: T): string {
        if (obj && typeof obj === 'object' && 'toJSON' in obj) {
            return JSON.stringify((obj as any).toJSON());
        }
        return JSON.stringify(obj);
    }
    
    static deserialize<T>(json: string, targetClass?: new (...args: any[]) => T): T {
        const data = JSON.parse(json);
        if (targetClass && 'fromJSON' in targetClass) {
            return (targetClass as any).fromJSON(data);
        }
        return data;
    }
}

// Using serializer
const user2 = new User(2, "Bob", "bob@example.com");
const json = Serializer.serialize(user2);
console.log(json);

const user3 = Serializer.deserialize(json, User);
console.log(user3);

// Serialization with validation
class ValidatedSerializer {
    static serialize<T>(obj: T): string {
        return JSON.stringify(obj);
    }
    
    static deserialize<T>(json: string, validator?: (data: any) => boolean): T | null {
        try {
            const data = JSON.parse(json);
            if (validator && !validator(data)) {
                throw new Error("Validation failed");
            }
            return data;
        } catch (error) {
            console.error("Deserialization error:", error);
            return null;
        }
    }
}
Coding Round
93. Type Assertions in TypeScript

Type assertions and type casting using as and angle bracket syntax.

  • as: value as string
  • Angle bracket: <string>value
  • Non-null: value!
  • Double assertion: value as any as string
typescript
// Type Assertions and Type Casting in TypeScript
// Type assertion with as
let someValue: any = "Hello TypeScript";
let strLength: number = (someValue as string).length;
console.log(strLength);

// Type assertion with angle bracket
let strLength2: number = (<string>someValue).length;
console.log(strLength2);

// Type assertion with unknown
let unknownValue: unknown = "Hello";
let stringValue: string = unknownValue as string;

// Type assertion for DOM elements
const element = document.getElementById("app") as HTMLDivElement;

// Type assertion for objects
interface User {
    name: string;
    age: number;
}

const data: any = { name: "Alice", age: 25 };
const user = data as User;
console.log(user.name);

// Non-null assertion operator
let maybeString: string | null = "Hello";
let definitelyString: string = maybeString!;
console.log(definitelyString);

// Double assertion
let value: any = "Hello";
let numberValue: number = value as any as number;

// Type assertion with generics
function assertType<T>(value: any): T {
    return value as T;
}

const assertedUser = assertType<User>({ name: "Alice", age: 25 });
console.log(assertedUser.name);

// Type assertion vs type casting
interface Animal {
    name: string;
}

interface Dog extends Animal {
    breed: string;
}

const animal: Animal = { name: "Rex" };
const dog = animal as Dog; // Type assertion
// dog.breed // Error: undefined

// Type guard with assertion
function isString(value: any): value is string {
    return typeof value === "string";
}

function assertIsString(value: any): asserts value is string {
    if (typeof value !== "string") {
        throw new Error("Value is not a string");
    }
}

function processValue(value: any): string {
    assertIsString(value);
    return value.toUpperCase();
}

console.log(processValue("hello")); // HELLO
Coding Round
94. Mixins in TypeScript

Mixins for composition using TypeScript's class merging capabilities.

  • applyMixins: function applyMixins(derivedCtor: any, baseCtors: any[])
  • Class mixins: function Timestamped<TBase extends Constructor>(Base: TBase)
  • Functional mixins: function mixin<T extends new (...args: any[]) => any>(...mixins: any[])
  • Decorator-based: @mixin(Disposable, Activatable)
typescript
// Mixins in TypeScript
// Mixin pattern
function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

// Base classes
class Disposable {
    isDisposed: boolean = false;
    
    dispose(): void {
        this.isDisposed = true;
        console.log("Disposed");
    }
}

class Activatable {
    isActive: boolean = false;
    
    activate(): void {
        this.isActive = true;
        console.log("Activated");
    }
    
    deactivate(): void {
        this.isActive = false;
        console.log("Deactivated");
    }
}

// Class using mixins
class SmartObject implements Disposable, Activatable {
    isDisposed: boolean = false;
    isActive: boolean = false;
    
    dispose: () => void;
    activate: () => void;
    deactivate: () => void;
}

applyMixins(SmartObject, [Disposable, Activatable]);

// Usage
const smartObj = new SmartObject();
smartObj.activate();
smartObj.dispose();

// Alternative: Function mixins
type Constructor<T = {}> = new (...args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        timestamp = new Date();
        
        getTimestamp(): string {
            return this.timestamp.toISOString();
        }
    };
}

function Versioned<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        version = 1;
        
        incrementVersion(): void {
            this.version++;
        }
    };
}

class BaseUser {
    name: string;
    
    constructor(name: string) {
        this.name = name;
    }
}

const TimestampedUser = Timestamped(BaseUser);
const VersionedTimestampedUser = Versioned(TimestampedUser);

const user = new VersionedTimestampedUser("Alice");
console.log(user.name);
console.log(user.getTimestamp());
console.log(user.version);
user.incrementVersion();
console.log(user.version);

// Functional mixins
function mixin<T extends new (...args: any[]) => any>(...mixins: any[]) {
    return (target: T) => {
        applyMixins(target, mixins);
        return target;
    };
}

// Using decorator-based mixins
@mixin(Disposable, Activatable)
class AnotherSmartObject implements Disposable, Activatable {
    isDisposed: boolean = false;
    isActive: boolean = false;
    
    dispose: () => void;
    activate: () => void;
    deactivate: () => void;
}
Coding Round
95. Type Guards in TypeScript

Type guards for runtime type checking using typeof, instanceof, and custom predicates.

  • typeof: typeof value === "string"
  • instanceof: value instanceof Dog
  • Custom predicate: function isString(value: any): value is string
  • Discriminated union: kind property
typescript
// Type Guards in TypeScript
// typeof type guard
function isString(value: any): value is string {
    return typeof value === "string";
}

function isNumber(value: any): value is number {
    return typeof value === "number";
}

function isBoolean(value: any): value is boolean {
    return typeof value === "boolean";
}

// instanceof type guard
class Animal {
    name: string = "";
}

class Dog extends Animal {
    breed: string = "";
}

class Cat extends Animal {
    color: string = "";
}

function isDog(animal: Animal): animal is Dog {
    return animal instanceof Dog;
}

// Custom type guard with predicate
interface User {
    name: string;
    email: string;
}

interface Admin {
    name: string;
    role: string;
    permissions: string[];
}

function isAdmin(user: User | Admin): user is Admin {
    return (user as Admin).role !== undefined;
}

// Discriminated union
interface Square {
    kind: "square";
    size: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

type Shape = Square | Circle | Rectangle;

function isSquare(shape: Shape): shape is Square {
    return shape.kind === "square";
}

function isCircle(shape: Shape): shape is Circle {
    return shape.kind === "circle";
}

// Usage
function processValue(value: string | number): string {
    if (isString(value)) {
        return `String: ${value}`;
    }
    return `Number: ${value}`;
}

function handleAnimal(animal: Animal): string {
    if (isDog(animal)) {
        return `Dog: ${animal.name}, ${animal.breed}`;
    }
    return `Animal: ${animal.name}`;
}

function handleUser(user: User | Admin): string {
    if (isAdmin(user)) {
        return `Admin: ${user.name}, Role: ${user.role}`;
    }
    return `User: ${user.name}, Email: ${user.email}`;
}

function handleShape(shape: Shape): number {
    if (isSquare(shape)) {
        return shape.size * shape.size;
    }
    if (isCircle(shape)) {
        return Math.PI * shape.radius * shape.radius;
    }
    return shape.width * shape.height;
}

// Type guard with array
function isArrayOfStrings(value: any[]): value is string[] {
    return value.every(item => typeof item === "string");
}
Coding Round
96. Type Predicates in TypeScript

Type predicates for custom type narrowing using is keyword.

  • Basic predicate: value is string
  • Interface predicate: value is HasName
  • Array predicate: value is string[]
  • Class predicate: value is Person
typescript
// Type Predicates in TypeScript
// Basic type predicate
function isString(value: any): value is string {
    return typeof value === "string";
}

// Type predicate with interface
interface HasName {
    name: string;
}

function hasName(value: any): value is HasName {
    return value && typeof value.name === "string";
}

// Type predicate with union
type Status = "active" | "inactive" | "pending";

function isActiveStatus(status: string): status is "active" {
    return status === "active";
}

// Type predicate with array
function isStringArray(value: any[]): value is string[] {
    return value.every(item => typeof item === "string");
}

// Type predicate with object
function isUser(value: any): value is User {
    return value && 
           typeof value.name === "string" && 
           typeof value.age === "number";
}

// Usage
function processValue(value: any): string {
    if (isString(value)) {
        return value.toUpperCase();
    }
    return "Not a string";
}

function processUser(value: any): string {
    if (isUser(value)) {
        return `User: ${value.name}, Age: ${value.age}`;
    }
    return "Not a user";
}

function processStatus(status: string): string {
    if (isActiveStatus(status)) {
        return "Status is active";
    }
    return "Status is not active";
}

// Type predicate in filter
const mixedArray: any[] = ["hello", 42, "world", true, "typescript"];
const stringsOnly = mixedArray.filter(isString);
console.log(stringsOnly); // ["hello", "world", "typescript"]

// Type predicate with class
class Person {
    constructor(public name: string, public age: number) {}
}

function isPerson(value: any): value is Person {
    return value instanceof Person;
}

const person = new Person("Alice", 25);
if (isPerson(person)) {
    console.log(person.name);
}

// Type predicate with generic
function isType<T>(value: any, constructor: new (...args: any[]) => T): value is T {
    return value instanceof constructor;
}

const date = new Date();
if (isType(date, Date)) {
    console.log(date.getFullYear());
}
Coding Round
97. Advanced Types in TypeScript

Advanced TypeScript types including conditional, mapped, and template literal types.

  • Conditional: T extends U ? X : Y
  • Mapped: { [P in keyof T]: T[P] }
  • Template literal: Hello, ${string}
  • Recursive: type DeepReadonly<T>
typescript
// Advanced Types in TypeScript
// Conditional types
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>; // false

// Infer keyword
type ElementType<T> = T extends (infer U)[] ? U : never;
type C = ElementType<string[]>; // string
type D = ElementType<number>; // never

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

type Partial<T> = {
    [P in keyof T]?: T[P];
};

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

// Template literal types
type Greeting = `Hello, ${string}`;
type Color = "red" | "green" | "blue";
type ColorMessage = `Color: ${Color}`;

// Recursive types
type JSONValue = string | number | boolean | null | JSONObject | JSONArray;
interface JSONObject {
    [key: string]: JSONValue;
}
type JSONArray = JSONValue[];

// Omit type
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// Exclude and Extract
type Status = "active" | "inactive" | "pending";
type Active = Exclude<Status, "inactive" | "pending">; // "active"
type Pending = Extract<Status, "pending" | "active">; // "pending" | "active"

// NonNullable
type NonNullable<T> = T extends null | undefined ? never : T;

// ReturnType
function getUser() {
    return { name: "Alice", age: 25 };
}
type UserType = ReturnType<typeof getUser>; // { name: string; age: number }

// Parameters
function greet(name: string, age: number): string {
    return `Hello ${name}, age ${age}`;
}
type GreetParams = Parameters<typeof greet>; // [string, number]

// Usage
interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type UserName = Pick<User, "name" | "email">;
type UserWithoutId = Omit<User, "id">;
Coding Round
98. DOM Manipulation in TypeScript

Type-safe DOM manipulation using TypeScript with HTML elements.

  • Element selection: document.getElementById("id") as HTMLDivElement
  • Event handling: addEventListener("click", (event: MouseEvent) => { })
  • Generic functions: function getElement<T extends HTMLElement>(id: string): T
  • Type-safe creation: createCustomElement<T>(tagName: string, options: Partial<T>): T
typescript
// TypeScript with DOM Manipulation
// Getting elements
const element = document.getElementById("app") as HTMLDivElement;
const elements = document.querySelectorAll(".item") as NodeListOf<HTMLElement>;
const button = document.querySelector<HTMLButtonElement>("#submit")!;

// Creating elements
const newDiv = document.createElement("div");
newDiv.className = "container";
newDiv.innerHTML = "<p>Hello World</p>";
document.body.appendChild(newDiv);

// Event handling
element.addEventListener("click", (event: MouseEvent) => {
    console.log("Clicked at", event.clientX, event.clientY);
});

// Typed event handlers
function handleInput(event: Event): void {
    const input = event.target as HTMLInputElement;
    console.log("Input value:", input.value);
}

const input = document.querySelector<HTMLInputElement>("#input");
input?.addEventListener("input", handleInput);

// Form handling
interface FormData {
    name: string;
    email: string;
    age: number;
}

function handleFormSubmit(event: SubmitEvent): void {
    event.preventDefault();
    const form = event.target as HTMLFormElement;
    const formData = new FormData(form);
    
    const data: FormData = {
        name: formData.get("name") as string,
        email: formData.get("email") as string,
        age: Number(formData.get("age"))
    };
    
    console.log("Form data:", data);
}

const form = document.querySelector<HTMLFormElement>("#form");
form?.addEventListener("submit", handleFormSubmit);

// Fetch with TypeScript
interface UserData {
    id: number;
    name: string;
    email: string;
}

async function fetchUser(id: number): Promise<UserData> {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json() as UserData;
}

// DOM manipulation with generics
function getElement<T extends HTMLElement>(id: string): T {
    const element = document.getElementById(id);
    if (!element) {
        throw new Error(`Element with id ${id} not found`);
    }
    return element as T;
}

// Type-safe class list
function toggleClass(element: HTMLElement, className: string): void {
    element.classList.toggle(className);
}

// Custom element creation
function createCustomElement<T extends HTMLElement>(
    tagName: string,
    options: Partial<T>
): T {
    const element = document.createElement(tagName) as T;
    Object.assign(element, options);
    return element;
}
Coding Round
99. Advanced Generics in TypeScript

Advanced generics including constraints, keyof, conditional types, and recursive types.

  • Constraints: <T extends object>
  • keyof: <T, K extends keyof T>
  • Conditional: type Flatten<T> = T extends any[] ? T[number] : T
  • Recursive: type DeepReadonly<T>
typescript
// Advanced Generics in TypeScript
// Generic constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

// Generic with multiple constraints
function mergeObjects<T extends object, U extends object>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

// Generic with default type
function createArray<T = string>(length: number, value: T): T[] {
    return Array(length).fill(value);
}

// Generic with keyof
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
    return items.map(item => item[key]);
}

// Generic with conditional types
type Flatten<T> = T extends any[] ? T[number] : T;

// Generic with recursive types
type DeepReadonly<T> = {
    readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// Generic with tuple types
type FirstElement<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type LastElement<T extends any[]> = T extends [...any[], infer L] ? L : never;

// Generic with function types
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;

// Usage
interface User {
    id: number;
    name: string;
    age: number;
}

const user = { id: 1, name: "Alice", age: 25 };
console.log(getProperty(user, "name"));

const merged = mergeObjects({ name: "Alice" }, { age: 25 });
console.log(merged);

const stringArray = createArray(3, "Hello");
const numberArray = createArray<number>(3, 42);

const users: User[] = [
    { id: 1, name: "Alice", age: 25 },
    { id: 2, name: "Bob", age: 30 }
];
const names = pluck(users, "name");
console.log(names);

type UserType = Flatten<User[]>; // User

type DeepReadonlyUser = DeepReadonly<{
    id: number;
    name: string;
    address: {
        city: string;
        zip: string;
    };
}>;

type First = FirstElement<[1, 2, 3]>; // 1
type Last = LastElement<[1, 2, 3]>; // 3

type Params = Parameters<(name: string, age: number) => string>; // [string, number]
type Return = ReturnType<(name: string, age: number) => string>; // string
Coding Round
100. TypeScript Best Practices

Best practices for writing clean, type-safe TypeScript code.

  • Explicit types: Use for function parameters and returns
  • Interfaces: Use for object shapes
  • Type guards: Use for type narrowing
  • readonly: Use for immutable properties
  • Utility types: Partial, Readonly, Pick, Omit
typescript
// TypeScript Best Practices
// 1. Use explicit types for function parameters and returns
function add(a: number, b: number): number {
    return a + b;
}

// 2. Use interfaces for object shapes
interface User {
    id: number;
    name: string;
    email: string;
}

// 3. Use type guards for type narrowing
function isUser(value: any): value is User {
    return value && typeof value.name === "string";
}

// 4. Use readonly for immutable properties
interface ReadonlyUser {
    readonly id: number;
    name: string;
}

// 5. Use optional properties for optional fields
interface PartialUser {
    name?: string;
    email?: string;
}

// 6. Use union types for multiple possible types
type Status = "active" | "inactive" | "pending";

// 7. Use generics for reusable code
function identity<T>(value: T): T {
    return value;
}

// 8. Use type assertions sparingly
const element = document.getElementById("app") as HTMLDivElement;

// 9. Use enum for constants
enum HttpStatus {
    OK = 200,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404
}

// 10. Use strict mode in tsconfig
// "strict": true

// 11. Use interfaces for function types
interface GreetFunction {
    (name: string): string;
}

// 12. Use index signatures for dynamic objects
interface StringMap {
    [key: string]: string;
}

// 13. Use utility types
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type PickUser = Pick<User, "name" | "email">;

// 14. Use never for unreachable code
function throwError(message: string): never {
    throw new Error(message);
}

// 15. Use void for functions with no return
function log(message: string): void {
    console.log(message);
}

// 16. Use unknown for uncertain types
let uncertain: unknown = "Hello";
if (typeof uncertain === "string") {
    console.log(uncertain.toUpperCase());
}

// 17. Use async/await for promises
async function fetchData(): Promise<User> {
    const response = await fetch("/api/user");
    return response.json();
}

// 18. Use const assertions
const config = {
    apiUrl: "https://api.example.com",
    timeout: 5000
} as const;

// 19. Use non-null assertion only when sure
const button = document.getElementById("submit")!;

// 20. Use type imports for better performance
// import type { User } from "./types";