All topics
Frontend · Learning hub

Typescript-advanced notes for developers

Master Typescript-advanced with a curated set of 2 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore Frontend notes
Typescript-advanced

TypeScript Advanced Types

TypeScript Advanced Types Generics // Generic functions function identity<T>(arg: T): T { return arg; } function first<T>(arr: T[]): T | undefined { return arr[

TypeScript Advanced Types

Generics

// Generic functions
function identity<T>(arg: T): T { return arg; }
function first<T>(arr: T[]): T | undefined { return arr[0]; }

// Constrained generics
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
getProperty({ name: 'Alice', age: 30 }, 'name');  // OK
getProperty({ name: 'Alice' }, 'foo');             // Error

// Multiple type params
function zip<A, B>(a: A[], b: B[]): [A, B][] {
  return a.map((item, i) => [item, b[i]]);
}

// Generic classes
class Repository<T extends { id: string }> {
  private items: T[] = [];
  findById(id: string): T | undefined {
    return this.items.find(i => i.id === id);
  }
  save(item: T): void { this.items.push(item); }
}

// Generic interfaces
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}
type UserResponse = ApiResponse<User>;
type ListResponse<T> = ApiResponse<{ items: T[]; total: number }>;

// Default generic params
interface State<T = unknown> { value: T; loading: boolean; }

Utility Types

interface User {
  id: string;
  name: string;
  email: string;
  age?: number;
  role: 'admin' | 'user';
}

// Built-in utility types
Partial<User>           // all props optional
Required<User>          // all props required
Readonly<User>          // all props readonly
Pick<User, 'id'|'name'> // subset of props
Omit<User, 'role'>      // exclude props
Record<string, User>    // key → value map
Exclude<'a'|'b'|'c', 'a'>  // 'b' | 'c'
Extract<'a'|'b'|'c', 'a'|'b'>  // 'a' | 'b'
NonNullable<string|null|undefined>  // string
ReturnType<typeof fn>   // return type of fn
Parameters<typeof fn>   // param types as tuple
InstanceType<typeof MyClass>
Awaited<Promise<string>>  // string

// Custom utility types
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

type Mutable<T> = { -readonly [K in keyof T]: T[K] };

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
// Usage: Optional<User, 'age' | 'role'> — makes those fields optional

type CreateInput<T> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;

Conditional & Mapped Types

// Conditional types
type IsString<T> = T extends string ? true : false;
type IsArray<T> = T extends any[] ? true : false;

// infer — extract type within conditional
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
type ArrayElement<T> = T extends (infer E)[] ? E : never;

type UnwrapPromise<T> = T extends Promise<infer R> ? UnwrapPromise<R> : T;
// UnwrapPromise<Promise<Promise<string>>> → string

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

// Mapped types
type Nullable<T> = { [K in keyof T]: T[K] | null };
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
// Getters<{name: string}> → { getName: () => string }

// Template literal types
type EventName<T extends string> = `on${Capitalize<T>}`;
type HttpMethod = 'get' | 'post' | 'put' | 'delete';
type ApiRoute = `/api/${string}`;

// Discriminated unions
type Result<T> =
  | { success: true; data: T }
  | { success: false; error: string };

function processResult<T>(result: Result<T>) {
  if (result.success) {
    console.log(result.data);   // T — TypeScript knows this
  } else {
    console.log(result.error);  // string
  }
}

Decorators & Advanced Patterns

// satisfies operator (TS 4.9) — validate type without widening
const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
} satisfies Record<string, string | number[]>;
palette.red.map(...)    // OK — type is number[], not string | number[]

// const assertion
const routes = ['/', '/about', '/contact'] as const;
type Route = typeof routes[number];  // '/' | '/about' | '/contact'

const config = { host: 'localhost', port: 3000 } as const;
// config.port is 3000 (literal), not number

// Function overloads
function parse(val: string): number;
function parse(val: number): string;
function parse(val: string | number): string | number {
  return typeof val === 'string' ? parseInt(val) : val.toString();
}

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

// Branded types — prevent mixing compatible primitives
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
const toUserId = (id: string): UserId => id as UserId;
// Can't pass OrderId where UserId expected
Typescript-advanced

TypeScript Advanced Interview Questions

TypeScript Advanced Interview Questions Difference between interface and type alias? Interface: extendable via extends/implements, declaration merging. Type: ca

TypeScript Advanced Interview Questions

  • Difference between interface and type alias? Interface: extendable via extends/implements, declaration merging. Type: can express unions, intersections, mapped types, template literals. Prefer interface for objects/classes; type for complex types

  • What is structural typing? TS uses duck typing — if an object has all required properties, it satisfies the type regardless of its class/origin. Contrast with nominal typing (Java/C#) where types must explicitly declare conformance

  • What is the infer keyword? Used in conditional types to capture a type variable: T extends Promise<infer R> ? R : never extracts the resolved type from a Promise

  • Covariance vs contravariance? Function return types are covariant (can return subtype). Function param types are contravariant (param must accept supertype). TypeScript function params are bivariant by default (use --strictFunctionTypes)

  • When to use unknown vs any? unknown is the type-safe any — you must narrow it before use. Use unknown for values you don't control (API responses, error catches). Avoid any; it disables type checking

  • What is a discriminated union? Union of types sharing a literal discriminant field (e.g., type: "circle" | "square"). TypeScript narrows the type based on the discriminant in switch/if statements

  • How to make a deep readonly type? Mapped type: type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K] }

Keep your Typescript-advanced knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever