Practice & Reference
Practice & Reference: Exercise 1 — Define TestCase Interface Build a TestCase Interface with Enum Define a string enum `Priority` with values LOW, MEDIUM, HIGH, CRITICAL.
Exercise 1 — Define TestCase Interface
Build a TestCase Interface with Enum
Define a string enum `Priority` with values LOW, MEDIUM, HIGH, CRITICAL. Define a string enum `TestStatus` with PASS, FAIL, SKIP, BLOCKED. Define an interface `TestCase` with: id (number, readonly), title (string), description (optional string), status (TestStatus), priority (Priority), tags (string array), durationMs (number), and assignee (optional string). Create two TestCase objects: one for a passing login test and one for a failing payment test.
String enums produce readable values ('PASS', 'CRITICAL') in logs and reports instead of opaque numbers. The readonly modifier on id prevents tests from accidentally changing an identifier. Optional fields (?) let you create minimal test objects without boilerplate, while required fields enforce a complete, valid contract.
Exercise 2 — Generic ApiResponse Wrapper
Generic API Response Wrapper with Type Guards
Create a generic interface `ApiResponse ` with fields: data (T | null), status (number), ok (boolean), error (string | null), requestId (string). Write a generic factory function `createApiResponse ` that takes data and status code and returns a correctly filled ApiResponse . Write a type guard function `isSuccessResponse ` that returns true if ok is true and data is not null. Write a `parseUserResponse` function that takes `ApiResponse ` and validates it is a user (has id: number, name: string, email: string). Test with a 200 user response and a 404 error response.
Generics allow one `ApiResponse ` interface to correctly type the data field as User, Product, Order, or any other type. The type guard narrows the type so TypeScript knows data is non-null after the check. The runtime validation function bridges the gap between 'unknown API data' and your typed interface — essential for safe API test assertions.
Exercise 3 — Typed Playwright POM Base Class
Generic Playwright POM Base Class with Fixtures
Create an abstract class `PageObjectBase` that accepts `Page` from Playwright in its constructor. It should have: a protected abstract `path` property (string), a `navigate()` method that calls `page.goto`, a generic `getElement (selector: string): T` method, a protected `waitForSelector(selector: string, state?: 'visible'|'hidden'|'attached'|'detached')` helper, and an `expectUrl(expected: string)` assertion helper. Then create a concrete `LoginPage` that extends it with typed `login(creds: {email:string, password:string})`, `getErrorText()`, and `isLoggedIn()` methods. Finally show a typed fixture extension using `test.extend `.
Abstract classes enforce that every page declares its own path, preventing forgetting to set the route. Protected access on helpers and locators keeps the public API clean — test code only sees navigate, login, getErrorText, isLoggedIn. The generic getElement preserves type information from Playwright's Locator hierarchy. The fixture extension pattern makes the typed page object available in every test that needs it without manually constructing it — this is the standard Playwright TypeScript pattern for large projects.
Quick Reference: TypeScript Features
Bookmark the TypeScript Playground at typescriptlang.org/play — paste any snippet and see the compiled JavaScript, type errors, and hover types instantly. It is the fastest way to experiment with TypeScript concepts without setting up a project.