← Back to Tutorials
18. TypeScript Refactor
Why TypeScript for Playwright
- Better autocomplete and IntelliSense
- Type safety catches locator/API errors at compile time
- Self-documenting code with interfaces
- Easier refactoring for large test suites
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.