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.

Try it: types in action

Interview questions