← Back to Tutorials

18. TypeScript Refactor

Why TypeScript for Playwright

Typing Standards

// Properly typed Page Objects
export class InventoryPage {
  constructor(private page: Page) {}

  async addItemToCart(itemName: string): Promise<void> {
    await this.page.getByRole('button', {
      name: `Add ${itemName}`,
    }).click();
  }

  async getCartCount(): Promise<number> {
    const text = await this.page.locator('.cart-badge').textContent();
    return parseInt(text || '0', 10);
  }
}

// Typed fixtures
import { test as base } from '@playwright/test';

interface MyFixtures {
  loggedInPage: Page;
  authToken: string;
}

export const test = base.extend<MyFixtures>({
  loggedInPage: async ({ browser }, use) => {
    const context = await browser.newContext({ storageState: 'auth.json' });
    const page = await context.newPage();
    await use(page);
    await context.close();
  },
  authToken: async ({ request }, use) => {
    const res = await request.post('/api/auth/login', {
      data: { username: 'admin', password: 'pass' }
    });
    const { token } = await res.json();
    await use(token);
  },
});

Refactoring Tests to TypeScript

// Before (JavaScript)
test('login', async ({ page }) => {
  await page.fill('#user-name', 'standard_user');
  await page.fill('#password', 'secret_sauce');
  await page.click('#login-button');
});

// After (TypeScript with Page Object)
test('login with POM', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.loginAs('standard_user');
  await expect(loginPage.isLoggedIn()).toBeTruthy();
});
💡 Tip: Rename .js files to .ts and add types incrementally. Playwright's built-in TypeScript support means no separate compilation step.
✏️ Exercise: Take a JavaScript test file and rewrite it in TypeScript. Add interfaces for test data and convert Page Objects to typed classes.