Q2 of 38 · TypeScript

Explain TypeScript generics with a Page Object example.

TypeScriptMidtypescript-genericspage-objecttype-safetyutility-types

Short answer

Short answer: Generics let you write a function or class that works with any type while keeping full type safety — the caller provides the concrete type at usage time. In test automation, generics appear in reusable Page Object base classes, typed API response wrappers, fixture factories, and assertion helpers.

Detail

Without generics, you'd write either any (losing type safety) or a separate implementation for every type (violating DRY). Generics give you a placeholder type parameter — conventionally T, K, or something descriptive — that gets resolved to a real type at the call site.

The classic test automation use case is a typed base Page Object:

class BasePage<TLocators extends Record<string, string>> {
  constructor(
    protected readonly page: Page,
    protected readonly locators: TLocators
  ) {}

  async fill(key: keyof TLocators, value: string) {
    await this.page.fill(this.locators[key], value);
  }
}

When LoginPage extends BasePage<typeof loginLocators>, calling fill("emailInput", "...") is type-checked — TypeScript knows "emailInput" is a valid key and will error at compile time if you typo the locator name.

Constraints (extends) narrow what types are acceptable. T extends Record<string, unknown> means "any T that is an object" — you get full autocomplete on properties while still being generic.

Utility types are essentially built-in generic type aliases: Partial<T>, Required<T>, Pick<T, K>, ReturnType<F>. Knowing at least five by name is table stakes for maintainable TypeScript test code.

// EXAMPLE

generic-api-wrapper.ts

import type { Page } from "@playwright/test";

// Generic API response wrapper for typed assertions
type ApiResponse<T> = {
  status: number;
  body: T;
  headers: Record<string, string>;
};

async function getTypedResponse<T>(
  page: Page,
  url: string
): Promise<ApiResponse<T>> {
  const response = await page.request.get(url);
  const body = (await response.json()) as T;
  return {
    status: response.status(),
    body,
    headers: response.headers(),
  };
}

// T is provided at the call site — autocomplete and type errors work
type UserProfile = { id: number; name: string; email: string };

const { body, status } = await getTypedResponse<UserProfile>(
  page,
  "/api/users/1"
);
console.log(body.name);  // typed as string
console.log(body.id);    // typed as number
// body.nonExistent;     // TypeScript error at compile time

// WHAT INTERVIEWERS LOOK FOR

Correct explanation of type parameters (placeholder resolved at usage), constraint syntax with extends, and a real test automation example beyond the abstract identity function. Candidates should know at least three built-in utility types by name.

// COMMON PITFALL

Explaining generics only through abstract examples (identity functions, swapping values) without connecting to test code. Interviewers in SDET roles want to know you use generics in fixtures, Page Objects, or response wrappers — not just that you understand the syntax.