level 01 / variables-and-types
Variables & Types
TypeScript's type system from the ground up — primitives, inference, unions, and the annotations that make automation code self-documenting.
Why TypeScript for Playwright?
Playwright’s own type definitions are TypeScript. Every page object, fixture, and config file benefits from types: autocomplete, compile-time errors before your test runner even starts, and self-documenting method signatures. Learning TypeScript for Playwright is not optional — it is the foundation.
Variable declarations
// const — immutable binding (use for almost everything)
const baseURL = 'https://www.saucedemo.com';
// let — reassignable (use for counters, state that changes)
let retryCount = 0;
retryCount = 3;
// var — function-scoped, hoisted. Never use in modern code.
Rule: default to const. Use let only when the variable must be
reassigned. Never use var.
Primitive types
const username: string = 'standard_user';
const timeout: number = 30_000; // ms; underscore separator is valid
const headless: boolean = true;
const handle: null = null; // intentional absence of value
let page: undefined; // declared but not yet assigned
// TypeScript infers the type from the value — annotations are optional:
const url = 'https://example.com'; // inferred: string
const retries = 3; // inferred: number
Key insight: Always prefer inference over explicit annotations when the type is obvious from the value. Explicit annotations add noise without adding information.
Union types
A variable that can be one of several types:
// Status can only be one of these strings
type TestStatus = 'pass' | 'fail' | 'skip';
const result: TestStatus = 'pass'; // OK
// const bad: TestStatus = 'retry'; // Error: not assignable
// Union with primitives
type Timeout = number | 'default';
const t1: Timeout = 5000;
const t2: Timeout = 'default';
// Null-safe union (common pattern with DOM queries)
const element: HTMLElement | null = document.getElementById('submit');
if (element !== null) {
element.click(); // TypeScript knows it's HTMLElement here
}
Literal types
// String literal — only this exact value
type Environment = 'staging' | 'production' | 'local';
// Numeric literal
type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;
// Const assertion — infer the narrowest type
const config = {
baseURL: 'https://saucedemo.com',
timeout: 30_000,
} as const;
// config.baseURL is 'https://saucedemo.com' (literal), not string
Type assertions
// Tell TypeScript "I know more than you do"
const input = document.getElementById('username') as HTMLInputElement;
input.value = 'standard_user'; // OK — HTMLInputElement has .value
// Non-null assertion — you know it won't be null at runtime
const btn = document.getElementById('login-btn')!;
btn.click();
Key insight: Type assertions bypass TypeScript’s checks. Use them sparingly and only when you have information the type system cannot infer — not to silence errors you don’t understand.