Menu

HTML & JS Topic

Top 30 TypeScript Interview Questions & Answers

TypeScript Fundamentals

1. What is TypeScript and why use it over JavaScript?

Answer: TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. Key benefits:

  • Static typing with type inference

  • Early error detection at compile time

  • Enhanced IDE support (autocomplete, refactoring)

  • Better documentation through type annotations

  • ECMAScript features (even if target JS doesn't support them)

  • Interfaces and generics for scalable architecture

typescript

// JavaScript vs TypeScript
// JavaScript: Runtime error
function add(a, b) { return a + b; }
add('5', 3); // '53' (unexpected)

// TypeScript: Compile-time error
function add(a: number, b: number): number { 
    return a + b; 
}
add('5', 3); // Error: Argument of type 'string' is not assignable

2. What are the basic types in TypeScript?

Answer: TypeScript extends JavaScript types:

typescript

// Primitive types
let isDone: boolean = false;
let count: number = 42;
let name: string = "TypeScript";

// Arrays
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // Generic syntax

// Tuples
let tuple: [string, number] = ["hello", 10];

// Enum
enum Color { Red, Green, Blue };
let c: Color = Color.Green;

// Any (opt-out of type checking)
let notSure: any = 4;
notSure = "maybe a string";

// Unknown (type-safe counterpart of any)
let uncertain: unknown = 30;

// Void (no return value)
function warn(): void { console.log("warning"); }

// Never (function never returns)
function error(message: string): never {
    throw new Error(message);
}

// Object (non-primitive)
let obj: object = { key: "value" };

3. Explain the difference between interface and type

Answer:

Aspect interface type
Extends Can be extended Can use intersections
Implements Can be implemented by class Cannot be implemented
Declaration merging Supported Not supported
Utility Better for object shapes More flexible (unions, tuples, primitives)
Performance Slightly faster in some cases Similar

typescript

// Interface
interface Animal {
    name: string;
}
interface Bear extends Animal {
    honey: boolean;
}

// Type alias
type Animal2 = {
    name: string;
};
type Bear2 = Animal2 & { 
    honey: boolean 
};

// Interface declaration merging
interface Window {
    title: string;
}
interface Window {
    ts: TypeScriptAPI;
}
// Result: Window has both title and ts properties

// Type cannot do this - would be duplicate identifier

4. What are generics and why are they useful?

Answer: Generics create reusable components that work with multiple types while maintaining type safety.

typescript

// Without generics - loses type information
function identity(arg: any): any {
    return arg;
}

// With generics - preserves type information
function identity<T>(arg: T): T {
    return arg;
}

// Usage
let output = identity<string>("myString"); // Type: string
let output2 = identity(42); // Type inference: number

// Generic interfaces
interface GenericIdentityFn<T> {
    (arg: T): T;
}

// Generic classes
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

// Constrained generics
function loggingIdentity<T extends { length: number }>(arg: T): T {
    console.log(arg.length);
    return arg;
}

5. What are union and intersection types?

Answer:

typescript

// Union type (A OR B)
type Status = "active" | "inactive" | "pending";
type ID = string | number;

function printId(id: ID) {
    if (typeof id === "string") {
        console.log(id.toUpperCase());
    } else {
        console.log(id.toFixed(2));
    }
}

// Intersection type (A AND B)
interface BusinessPartner {
    name: string;
    credit: number;
}
interface Identity {
    id: string;
    name: string;
}
type Employee = BusinessPartner & Identity;

const employee: Employee = {
    id: "123",
    name: "John",
    credit: 1000
};

Intermediate TypeScript

6. Explain type guards and narrowing

Answer: Type guards help TypeScript infer more specific types within conditional blocks.

typescript

// typeof guard
function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return " ".repeat(padding) + value;
    }
    return padding + value; // padding is string here
}

// instanceof guard
class Bird {
    fly() { console.log("flying"); }
}
class Fish {
    swim() { console.log("swimming"); }
}
function move(pet: Bird | Fish) {
    if (pet instanceof Bird) {
        pet.fly();
    } else {
        pet.swim();
    }
}

// in operator guard
interface Dog { bark(): void; }
interface Cat { meow(): void; }
function makeSound(animal: Dog | Cat) {
    if ("bark" in animal) {
        animal.bark();
    } else {
        animal.meow();
    }
}

// Custom type guard (user-defined)
function isString(x: any): x is string {
    return typeof x === "string";
}

// Discriminated unions
type Shape = 
    | { kind: "circle"; radius: number }
    | { kind: "rectangle"; width: number; height: number };

function area(s: Shape) {
    switch (s.kind) {
        case "circle": return Math.PI * s.radius ** 2;
        case "rectangle": return s.width * s.height;
    }
}

7. What are decorators and how do you use them?

Answer: Decorators are special declarations that can modify classes, methods, properties, or parameters.

typescript

// Class decorator
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

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

class Calculator {
    @log
    add(x: number, y: number): number {
        return x + y;
    }
}

// Property decorator
function format(formatString: string) {
    return function (target: any, propertyKey: string) {
        let value = target[propertyKey];
        const getter = () => value;
        const setter = (newVal: any) => {
            value = formatString.replace("{value}", newVal);
        };
        Object.defineProperty(target, propertyKey, {
            get: getter,
            set: setter
        });
    };
}

// Parameter decorator
function validate(target: any, propertyKey: string, parameterIndex: number) {
    // Validation logic here
}

8. Explain the keyof and typeof operators

Answer:

typescript

// keyof: Get keys of a type/interface
interface Person {
    name: string;
    age: number;
    location: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "location"

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

// typeof: Get type of a value
const person = { name: "Alice", age: 30 };
type PersonType = typeof person;
// Equivalent to: type PersonType = { name: string; age: number; }

// Combined usage
const settings = {
    theme: "dark",
    language: "en",
    version: "1.0"
} as const; // as const makes it readonly

type Settings = typeof settings;
// type Settings = { readonly theme: "dark"; readonly language: "en"; readonly version: "1.0"; }

type SettingKeys = keyof Settings;
// type SettingKeys = "theme" | "language" | "version"

9. What are conditional types?

Answer: Types that express non-uniform type mappings based on conditions.

typescript

// Basic conditional type
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false

// Extract from union
type ExtractType<T, U> = T extends U ? T : never;
type Result = ExtractType<string | number | boolean, string | number>;
// Result: string | number

// Exclude from union
type ExcludeType<T, U> = T extends U ? never : T;
type Result2 = ExcludeType<string | number | boolean, string>;
// Result2: number | boolean

// Infer keyword (extract type from generic)
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type Element = ArrayElementType<string[]>; // string

// ReturnType utility (built-in using conditional types)
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type FuncReturn = MyReturnType<() => string>; // string

// Flatten array type
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // string

10. Explain mapped types

Answer: Create new types by transforming properties of existing types.

typescript

// Make all properties optional
type Partial<T> = {
    [P in keyof T]?: T[P];
};

// Make all properties required
type Required<T> = {
    [P in keyof T]-?: T[P];
};

// Make all properties readonly
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

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

// Record type (create object type with specific key/value types)
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

// Custom mapped type
type Nullable<T> = {
    [P in keyof T]: T[P] | null;
};

interface User {
    name: string;
    age: number;
}
type NullableUser = Nullable<User>;
// { name: string | null; age: number | null; }

// Key remapping (TypeScript 4.1+)
type Getters<T> = {
    [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; }

Advanced TypeScript

11. What are declaration files (.d.ts) and ambient declarations?

Answer: Declaration files provide type information for existing JavaScript code.

typescript

// user.d.ts (declaration file)
declare interface User {
    id: number;
    name: string;
}
declare function getUser(id: number): User;
declare const DEFAULT_USER: User;

// Usage in .ts file
const user = getUser(1); // TypeScript knows this returns User

// Global declarations (global.d.ts)
declare global {
    interface Window {
        myApp: any;
    }
    const __DEV__: boolean;
}

// Module declarations
declare module "my-module" {
    export function doSomething(): void;
    export const value: number;
}

// Shorthand ambient modules (for CSS, images, etc.)
declare module "*.css";
declare module "*.png";
declare module "*.svg" {
    const content: string;
    export default content;
}

12. Explain variance in TypeScript (covariance, contravariance, bivariance)

Answer: How subtyping relates to generics/complex types.

typescript

// Covariance: Preserves subtype relationship (default for arrays)
type Animal = { name: string };
type Dog = Animal & { breed: string };

let animals: Animal[] = [];
let dogs: Dog[] = [];
animals = dogs; // OK - covariance
// dogs = animals; // Error

// Contravariance: Reverses subtype relationship (function parameters)
type AnimalProcessor = (animal: Animal) => void;
type DogProcessor = (dog: Dog) => void;

let processAnimal: AnimalProcessor = (a: Animal) => {};
let processDog: DogProcessor = (d: Dog) => {};

// processDog = processAnimal; // OK - contravariant in parameter position
// processAnimal = processDog; // Error

// Invariance: Requires exact type (mutable arrays)
// Bivariance: Both accepted (TypeScript's default for function parameters in --strict mode off)

// Use variance annotations (in/out) with conditional types
interface Container<in T> { // contravariant
    consume(value: T): void;
}
interface Producer<out T> { // covariant
    produce(): T;
}

13. What are template literal types?

Answer: (TypeScript 4.1+) Types based on template literal strings.

typescript

type EventName<T extends string> = `${T}Changed`;
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;

// With unions
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
type Alignment = `${VerticalAlignment}-${HorizontalAlignment}`;
// "top-left" | "top-center" | "top-right" | "middle-left" | ...

// Infer substitution positions
type ExtractVerb<T> = T extends `${infer Verb}${string}` ? Verb : never;
type Verb = ExtractVerb<"getUser" | "setUser" | "deleteUser">; // "get" | "set" | "delete"

// Advanced: Path manipulation
type Path = `/${string}`;
type ApiRoute = `/api/${string}`;

// Utility types with template literals
type Getter<T extends string> = `get${Capitalize<T>}`;
type Setters<T extends string> = `set${Capitalize<T>}`;

14. Explain the satisfies operator

Answer: (TypeScript 4.9+) Validates expression type without changing inferred type.

typescript

// Without satisfies - loses literal types
const colors = {
    red: "#FF0000",
    green: "#00FF00",
    blue: "#0000FF"
}; // Type: { red: string; green: string; blue: string }

// With satisfies - preserves literal types
const colors2 = {
    red: "#FF0000",
    green: "#00FF00",
    blue: "#0000FF"
} as const; // Type: { readonly red: "#FF0000"; readonly green: "#00FF00"; readonly blue: "#0000FF"; }

// satisfies operator (better for complex cases)
const colors3 = {
    red: "#FF0000",
    green: "#00FF00",
    blue: "#0000FF"
} satisfies Record<string, `#${string}`>;

// Use case: Partial type checking without widening
interface Config {
    width: number;
    height: number;
    color?: string;
}

const config = {
    width: 100,
    height: 200,
    color: "#FF0000"
} satisfies Partial<Config>;
// Type: { width: number; height: number; color: string }
// Not: Partial<Config> (which would make all properties optional)

15. What are readonly arrays and tuples?

Answer: Immutable versions of array/tuple types.

typescript

// Readonly array
const numbers: readonly number[] = [1, 2, 3];
// numbers.push(4); // Error
// numbers[0] = 10; // Error

// Readonly tuple
type Point = readonly [number, number];
const origin: Point = [0, 0];
// origin[0] = 1; // Error

// const assertions (more strict)
const arr = [1, 2, 3] as const;
// arr is readonly [1, 2, 3] - literal readonly tuple

// Readonly utility type
interface Book {
    title: string;
    author: string;
}
const book: Readonly<Book> = { title: "TS", author: "Microsoft" };
// book.title = "New Title"; // Error

// Deep readonly
type DeepReadonly<T> = {
    readonly [P in keyof T]: DeepReadonly<T[P]>;
};

TypeScript with Classes & OOP

16. What are abstract classes in TypeScript?

Answer: Classes that cannot be instantiated directly, meant to be subclassed.

typescript

abstract class Animal {
    abstract makeSound(): void;
    
    move(): void {
        console.log("moving...");
    }
}

class Dog extends Animal {
    makeSound(): void {
        console.log("bark");
    }
}

// const animal = new Animal(); // Error: Cannot create instance of abstract class
const dog = new Dog();
dog.makeSound(); // "bark"
dog.move(); // "moving..."

// Abstract properties
abstract class Department {
    abstract name: string;
    abstract printName(): void;
    
    constructor(protected employees: string[] = []) {}
    
    addEmployee(employee: string) {
        this.employees.push(employee);
    }
}

class ITDepartment extends Department {
    constructor(public name: string) {
        super();
    }
    
    printName(): void {
        console.log(`Department: ${this.name}`);
    }
}

17. Explain access modifiers (public, private, protected)

Answer: Control visibility of class members.

typescript

class Employee {
    public name: string; // Accessible anywhere (default)
    private salary: number; // Accessible only within class
    protected employeeId: string; // Accessible within class and subclasses
    
    constructor(name: string, salary: number) {
        this.name = name;
        this.salary = salary;
        this.employeeId = this.generateId();
    }
    
    private generateId(): string {
        return `EMP-${Date.now()}`;
    }
    
    public getSalaryInfo(): string {
        return `Salary: $${this.salary}`; // Can access private member
    }
}

class Manager extends Employee {
    constructor(name: string, salary: number, public department: string) {
        super(name, salary);
    }
    
    public getEmployeeInfo(): string {
        // Can access protected member
        return `${this.name} (${this.employeeId}) manages ${this.department}`;
        // Cannot access this.salary (private)
    }
}

// ECMAScript private fields (# syntax)
class ModernEmployee {
    #secretCode: string; // Truly private (runtime enforcement)
    
    constructor() {
        this.#secretCode = "1234";
    }
}

18. What are static members?

Answer: Members that belong to the class itself, not instances.

typescript

class MathUtils {
    static readonly PI = 3.14159;
    
    static calculateCircumference(radius: number): number {
        return 2 * this.PI * radius;
    }
    
    static createFromDiameter(diameter: number): MathUtils {
        return new MathUtils(diameter / 2);
    }
    
    constructor(public radius: number) {}
}

// Access via class name, not instance
console.log(MathUtils.PI);
console.log(MathUtils.calculateCircumference(10));

// Static blocks (TypeScript 4.4+)
class Database {
    static connection: string;
    
    static {
        // Initialize static members
        this.connection = "localhost:5432";
        console.log("Database initialized");
    }
}

19. Explain method overloading in TypeScript

Answer: Multiple function signatures for the same implementation.

typescript

class Calculator {
    // Overload signatures
    add(a: number, b: number): number;
    add(a: string, b: string): string;
    add(a: number, b: string): string;
    add(a: string, b: number): string;
    
    // Implementation signature (not publicly visible)
    add(a: number | string, b: number | string): number | string {
        if (typeof a === "number" && typeof b === "number") {
            return a + b;
        }
        return `${a}${b}`;
    }
}

const calc = new Calculator();
calc.add(1, 2); // number
calc.add("1", "2"); // string
calc.add(1, "2"); // string

// Function overloading
function formatDate(timestamp: number): string;
function formatDate(date: Date): string;
function formatDate(value: number | Date): string {
    const date = typeof value === "number" ? new Date(value) : value;
    return date.toISOString();
}

20. What are mixins in TypeScript?

Answer: Pattern for composing classes from multiple base classes.

typescript

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

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

function Activatable<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        isActive = false;
        
        activate() {
            this.isActive = true;
        }
        
        deactivate() {
            this.isActive = false;
        }
    };
}

// Base class
class User {
    constructor(public name: string) {}
}

// Apply mixins
const TimestampedActivatableUser = Timestamped(Activatable(User));

const user = new TimestampedActivatableUser("Alice");
console.log(user.name); // "Alice"
console.log(user.timestamp); // 1633046400000
user.activate();
console.log(user.isActive); // true

TypeScript Configuration & Tooling

21. What are the important compiler options in tsconfig.json?

Answer:

json

{
  "compilerOptions": {
    /* Basic Options */
    "target": "ES2020",           // JS version to compile to
    "module": "commonjs",         // Module system
    "lib": ["ES2020", "DOM"],     // Library files to include
    "outDir": "./dist",           // Output directory
    "rootDir": "./src",           // Source directory
    
    /* Strictness Options */
    "strict": true,               // Enable all strict options
    "noImplicitAny": true,        // Error on implied 'any'
    "strictNullChecks": true,     // Null/undefined checking
    "strictFunctionTypes": true,  // Strict function parameter checking
    "strictPropertyInitialization": true, // Class properties must be initialized
    
    /* Module Resolution */
    "moduleResolution": "node",   // How to resolve modules
    "baseUrl": "./",              // Base directory for module resolution
    "paths": {                    // Path mapping
      "@/*": ["src/*"]
    },
    "esModuleInterop": true,      // Better CommonJS/ES module interop
    "allowSyntheticDefaultImports": true,
    
    /* Advanced */
    "declaration": true,          // Generate .d.ts files
    "sourceMap": true,            // Generate source maps
    "experimentalDecorators": true, // Enable decorators
    "emitDecoratorMetadata": true,
    
    /* Type Checking */
    "noUnusedLocals": true,       // Error on unused locals
    "noUnusedParameters": true,   // Error on unused parameters
    "noImplicitReturns": true,    // Error if not all code paths return
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

22. Explain module resolution in TypeScript

Answer: How TypeScript finds files referenced in import statements.

typescript

// Classic vs Node resolution
// Classic: Looks for .ts, .d.ts, and follows tsconfig.json paths
// Node: Follows Node.js module resolution algorithm

// Relative imports
import { Component } from './components/Component';

// Non-relative imports (looks in node_modules)
import React from 'react';

// Path mapping (tsconfig.json)
{
  "paths": {
    "@app/*": ["src/app/*"],
    "@utils/*": ["src/utils/*"]
  }
}

// Usage
import { api } from '@app/api';
import { format } from '@utils/helpers';

// Module resolution strategies affect:
// - import statement resolution
// /// <reference path="..." /> directives
// BaseUrl and paths configuratio
n

23. What are triple-slash directives?

Answer: Single-line comments containing XML tags used as compiler directives.

typescript

/// <reference path="..." />       // Dependency declaration
/// <reference types="..." />      // Type dependency
/// <reference lib="..." />        // Built-in lib inclusion
/// <amd-module name="..." />      // AMD module name
/// <amd-dependency path="..." />  // AMD dependency (deprecated)

// Example: Including type definitions
/// <reference types="node" />
/// <reference path="custom.d.ts" />

// These are largely superseded by tsconfig.json and import statements
// Still useful for:
// - Global type declarations ordering
// - UMD module declarations
// - Legacy codebases

TypeScript with Frameworks

24. How does TypeScript work with React?

Answer:

typescript

// Functional components
interface Props {
    name: string;
    age?: number;
}

const UserCard: React.FC<Props> = ({ name, age = 18 }) => {
    return <div>{name} ({age})</div>;
};

// With hooks
const UserList: React.FC = () => {
    const [users, setUsers] = useState<User[]>([]);
    const [loading, setLoading] = useState<boolean>(false);
    
    useEffect(() => {
        fetchUsers();
    }, []);
    
    return (
        <div>
            {users.map(user => (
                <UserCard key={user.id} name={user.name} />
            ))}
        </div>
    );
};

// Class components
interface State {
    count: number;
}

class Counter extends React.Component<{}, State> {
    state: State = { count: 0 };
    
    increment = () => {
        this.setState(prev => ({ count: prev.count + 1 }));
    };
    
    render() {
        return (
            <button onClick={this.increment}>
                Count: {this.state.count}
            </button>
        );
    }
}

// Generic components
interface ListProps<T> {
    items: T[];
    renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{renderItem(item)}</li>
            ))}
        </ul>
    );
}

25. How to type API responses in TypeScript?

Answer:

typescript

// Basic interface
interface User {
    id: number;
    name: string;
    email: string;
}

// Using fetch with async/await
async function fetchUser(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    const data: User = await response.json();
    return data;
}

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

// Zod for runtime validation (recommended)
import { z } from 'zod';

const UserSchema = z.object({
    id: z.number(),
    name: z.string(),
    email: z.string().email(),
});

type User = z.infer<typeof UserSchema>;

async function fetchUserSafe(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    const data = await response.json();
    return UserSchema.parse(data); // Throws if invalid
}

// Partial types for updates
function updateUser(id: number, updates: Partial<User>) {
    return fetch(`/api/users/${id}`, {
        method: 'PATCH',
        body: JSON.stringify(updates)
    });
}

Advanced Patterns & Best Practices

26. What is the as const assertion?

Answer: Makes an object/reference deeply readonly with literal types.

typescript

// Without as const
const colors = ["red", "green", "blue"];
// Type: string[]

// With as const
const colors2 = ["red", "green", "blue"] as const;
// Type: readonly ["red", "green", "blue"]

// Object properties become readonly literals
const user = {
    name: "John",
    age: 30,
    address: {
        city: "NYC"
    }
} as const;

// user.name = "Jane"; // Error
// user.address.city = "LA"; // Error

// Use with typeof for type extraction
const ROUTES = {
    home: "/",
    about: "/about",
    contact: "/contact"
} as const;

type Route = typeof ROUTES[keyof typeof ROUTES];
// Type: "/" | "/about" | "/contact"

// Function that returns literal types
function getStatus() {
    return "success" as const;
}
// Return type: "success" (not string)

27. Explain the infer keyword in depth

Answer: Used in conditional types to extract and capture types.

typescript

// Extract return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Extract parameter types
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

// Extract array element type
type ArrayElement<T> = T extends (infer U)[] ? U : never;

// Extract promise type
type PromiseType<T> = T extends Promise<infer U> ? U : never;

// Extract first argument
type FirstArg<T> = T extends (arg: infer A, ...args: any[]) => any ? A : never;

// Extract property type
type PropType<T, K extends keyof T> = T extends { [P in K]: infer U } ? U : never;

// Recursive extraction
type DeepArrayElement<T> = 
    T extends (infer U)[] ? DeepArrayElement<U> : T;

// Multiple inference points
type FunctionComponents<T> = 
    T extends (...args: infer Args) => infer Return 
        ? { args: Args, return: Return } 
        : never;

28. What are branded/nominal types?

Answer: Creating distinct types for values with the same underlying structure.

typescript

// Using intersection with unique symbol
type UserId = string & { readonly brand: unique symbol };
type ProductId = string & { readonly brand: unique symbol };

function createUserId(id: string): UserId {
    return id as UserId;
}

function createProductId(id: string): ProductId {
    return id as ProductId;
}

const userId = createUserId("123");
const productId = createProductId("123");

// These are not interchangeable
// function getUser(id: UserId) { ... }
// getUser(productId); // Error

// Alternative with interfaces
interface Brand<T extends string> {
    readonly brand: T;
}

type Branded<T, B extends string> = T & Brand<B>;

type Euros = Branded<number, "Euros">;
type Dollars = Branded<number, "Dollars">;

function addEuros(a: Euros, b: Euros): Euros {
    return (a + b) as Euros;
}

// Runtime validation
function validateEuros(amount: number): Euros {
    if (amount < 0) throw new Error("Invalid amount");
    return amount as Euros;
}

29. How to implement recursive types?

Answer: Types that reference themselves.

typescript

// Recursive type alias
type Json = 
    | string
    | number
    | boolean
    | null
    | Json[]
    | { [key: string]: Json };

// Tree structure
type TreeNode<T> = {
    value: T;
    children: TreeNode<T>[];
};

// Linked list
type LinkedList<T> = {
    value: T;
    next: LinkedList<T> | null;
};

// Recursive type with condition
type DeepPartial<T> = {
    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// Recursive utility type for readonly
type DeepReadonly<T> = {
    readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// Nested arrays
type NestedArray<T> = T | NestedArray<T>[];

const nested: NestedArray<number> = [1, [2, [3, 4]], 5];

30. What are template literal types with inference patterns?

Answer: Advanced template literal manipulation.

typescript

// Parse URL pattern
type ParseUrl<T extends string> =
    T extends `${infer Protocol}://${infer Host}/${infer Path}`
        ? { protocol: Protocol, host: Host, path: Path }
        : never;

type Url = ParseUrl<"https://example.com/api/users">;
// { protocol: "https", host: "example.com", path: "api/users" }

// Extract route parameters
type ExtractParams<T extends string> =
    T extends `${string}:${infer Param}/${infer Rest}`
        ? Param | ExtractParams<Rest>
        : T extends `${string}:${infer Param}`
        ? Param
        : never;

type Params = ExtractParams<"/users/:id/posts/:postId">;
// "id" | "postId"

// String manipulation
type Capitalize<S extends string> = 
    S extends `${infer First}${infer Rest}` 
        ? `${Uppercase<First>}${Rest}` 
        : S;

type Uncapitalize<S extends string> = 
    S extends `${infer First}${infer Rest}` 
        ? `${Lowercase<First>}${Rest}` 
        : S;

// Join string tuple
type Join<T extends string[], D extends string> =
    T extends [] ? '' :
    T extends [infer F] ? F :
    T extends [infer F, ...infer R]
        ? F extends string
            ? `${F}${D}${Join<R extends string[] ? R : [], D>}`
            : never
        : string;

Bonus: Common TypeScript Gotchas & Solutions

1. Excess Property Checking

typescript

interface Point { x: number; y: number; }
const p: Point = { x: 1, y: 2, z: 3 }; // Error

// Solutions:
const obj = { x: 1, y: 2, z: 3 };
const p2: Point = obj; // OK - fresh object
const p3: Point = { x: 1, y: 2, z: 3 } as Point; // Type assertion

2. Index Signatures vs Record

typescript

// Index signature (all string keys must be number)
interface StringArray {
    [index: string]: number;
}

// Record utility (same but more readable)
type StringArray2 = Record<string, number>;

3. Type vs Interface for Functions

typescript

// Interface for functions
interface SearchFunc {
    (source: string, subString: string): boolean;
}

// Type alias for functions
type SearchFunc2 = (source: string, subString: string) => boolean;

 

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?