Olvidarse de await antes de una aserción de Playwright produce "Argument of type 'Locator' is not assignable to parameter of type 'string'" en tiempo de ejecución; TypeScript muestra el mismo error en el editor antes de que corra el test. Playwright compila TypeScript internamente sin un paso de build separado, y el autocompletado que habilita significa que no necesitas memorizar la API. Este artículo cubre las adiciones de TypeScript que importan en código de tests: anotaciones de tipo, interfaces para datos de prueba, Page Objects tipados, y los cuatro errores que vas a ver con más frecuencia.

Qué agrega TypeScript a JavaScript

TypeScript es JavaScript con tipos. Todo lo que sabes de JavaScript funciona en TypeScript. Estás añadiendo una capa de anotaciones encima.

// JavaScript
function getUser(id) {
  return fetchUser(id);
}

// TypeScript — misma función con tipos
function getUser(id: number): Promise<User> {
  return fetchUser(id);
}

La anotación : number le dice a TypeScript que id debe ser un número. El : Promise le dice qué devuelve la función. Si accidentalmente pasas un string donde se espera un número, TypeScript te lo dice de inmediato, antes de que corra el código.

Por qué TypeScript importa específicamente para Playwright

Autocompletado

Cuando escribes page. en VS Code, TypeScript conoce todos los métodos que existen en page y te los muestra. No necesitas memorizar la API.

Detectar await faltante

¿Olvidaste await antes de una aserción? TypeScript te dice:

Argument of type 'Locator' is not assignable to parameter of type 'string'

Seguridad en Page Objects

Cuando creas una clase Page Object y llamas a sus métodos, TypeScript verifica que el método existe y que estás pasando los tipos correctos. Un error de tipeo en un nombre de método se detecta de inmediato.

Los tipos de Playwright son excelentes

El equipo de Playwright mantiene las definiciones de tipos de TypeScript como parte principal del proyecto. El autocompletado y la verificación de tipos funcionan desde el primer momento sin configuración adicional.

Configurar TypeScript para Playwright

Al inicializar un proyecto de Playwright, elegir TypeScript es una de las opciones:

npm init playwright@latest

Elige TypeScript cuando te lo pregunte. Esto crea playwright.config.ts (archivo de configuración en TypeScript), tests/example.spec.ts (test de ejemplo en TypeScript) y tsconfig.json (configuración de TypeScript).

Si ya tienes un proyecto en JavaScript, puedes convertirlo renombrando los archivos .js a .ts y añadiendo un tsconfig.json.

Sintaxis básica de TypeScript para código de tests

Anotaciones de tipo

// Tipos primitivos
const email: string = 'admin@becomeqa.com';
const timeout: number = 30000;
const isLoggedIn: boolean = false;

// En la práctica, TypeScript los infiere — rara vez necesitás escribirlos
const email = 'admin@becomeqa.com'; // TypeScript sabe que esto es un string

TypeScript infiere tipos a partir de valores. Solo necesitas escribir anotaciones de tipo cuando TypeScript no puede determinarlo por su cuenta.

Interfaces: definir la forma de los objetos

// Definir cómo se ve un objeto usuario
interface User {
  email: string;
  password: string;
  role: 'admin' | 'viewer';
}

// Ahora TypeScript sabe qué esperar
const testUser: User = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
  role: 'admin',
};

// TypeScript dará error si añadís un campo que no existe
const badUser: User = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
  role: 'admin',
  name: 'Alice', // Error: 'name' does not exist in type 'User'
};

Las interfaces en tests son más útiles para datos de prueba y parámetros de constructor de Page Objects.

Tipos unión

// 'admin' | 'viewer' significa que el valor solo puede ser uno de estos dos strings
type UserRole = 'admin' | 'viewer';

// Útil para campos de estado, tipos que tienen un conjunto fijo de valores
type ItemStatus = 'Planned' | 'In Progress' | 'Completed';

function selectStatus(status: ItemStatus) {
  // TypeScript sabe que status solo puede ser uno de tres valores
}

selectStatus('Planned');    // ✓ válido
selectStatus('Cancelled'); // ✗ Error: not assignable to type 'ItemStatus'

Los tipos unión evitan que valores inválidos lleguen a tus datos de prueba.

Propiedades opcionales

interface TestItem {
  destination: string;
  status: ItemStatus;
  notes?: string; // El ? significa que esta propiedad es opcional
}

// Ambos son válidos
const item1: TestItem = { destination: 'Tokyo', status: 'Planned' };
const item2: TestItem = { destination: 'París', status: 'Completed', notes: 'Visitado' };

TypeScript en el Page Object Model

El valor real de TypeScript para los QA engineers aparece en los Page Objects:

import { Page } from '@playwright/test';

// Importar el tipo Page de Playwright
export class LoginPage {
  // TypeScript sabe que page es un objeto Page de Playwright
  constructor(private page: Page) {}

  async login(email: string, password: string): Promise<void> {
    await this.page.getByRole('button', { name: 'Login' }).click();
    await this.page.getByLabel('Username').fill(email);
    await this.page.getByLabel('Password').fill(password);
    await this.page.getByRole('button', { name: 'Submit' }).click();
  }

  async getErrorMessage(): Promise<string | null> {
    const error = this.page.getByRole('alert');
    if (await error.isVisible()) {
      return error.textContent();
    }
    return null;
  }
}

Cuando usas esta clase en un test:

test('login con credenciales inválidas', async ({ page }) => {
  const loginPage = new LoginPage(page);
  
  // TypeScript sabe que login() toma dos strings
  await loginPage.login('wrong@example.com', 'wrongpass');
  
  // TypeScript sabe que getErrorMessage() devuelve Promise<string | null>
  const error = await loginPage.getErrorMessage();
  expect(error).toBeTruthy();
});

Si escribes loginPage.loginn() (error de tipeo), TypeScript lo detecta de inmediato. Si llamas loginPage.login(123, 'password') (tipo incorrecto), TypeScript da error.

Los tipos incorporados de Playwright que vas a usar

import { Page, BrowserContext, Locator, APIRequestContext } from '@playwright/test';

// Page — el objeto principal de página del navegador
// BrowserContext — una sesión aislada del navegador
// Locator — una referencia a elementos de la página
// APIRequestContext — para testing de API

Los importas al escribir Page Objects tipados o archivos de fixtures tipados.

Fixtures de tests tipados

Cuando extiendes los fixtures de Playwright (para compartir page objects entre tests), los tipos de TypeScript lo hacen confiable:

import { test as base, Page } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';

// Definir el tipo de tus fixtures personalizados
type Fixtures = {
  loginPage: LoginPage;
};

// Extender el test base con fixtures tipados
export const test = base.extend<Fixtures>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await use(loginPage);
  },
});

// En los tests, TypeScript sabe que loginPage es una instancia de LoginPage
test('test con fixture', async ({ loginPage }) => {
  await loginPage.login('admin@becomeqa.com', 'testpass123');
});

tsconfig.json: lo que importa

Cuando npm init playwright@latest crea un tsconfig.json, funciona desde el primer momento. La configuración que vale conocer:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "strict": true,       // ← Esta es la importante
    "esModuleInterop": true,
    "outDir": "./dist"
  }
}

"strict": true habilita todas las verificaciones de estrictez de TypeScript: sin tipo any implícito, verificaciones de null (TypeScript avisa cuando podrías estar llamando métodos sobre null) y la verificación que detecta Promises sin await.

Mantén strict: true. Las advertencias que genera casi siempre apuntan a problemas reales.

Lo que no necesitás aprender (todavía)

TypeScript tiene características útiles para el desarrollo de aplicaciones a gran escala pero que rara vez se necesitan en código de tests:

  • Genéricos: a menos que estés construyendo utilidades de fixtures reutilizables
  • Decoradores: usados en frameworks más viejos, no en Playwright moderno
  • Tipos utilitarios (Partial, Readonly): ocasionalmente útiles, no requeridos
  • Archivos de declaración (.d.ts): para autores de librerías, no para escritores de tests

Apréndelos cuando te encuentres con un problema específico que resuelven.

Errores comunes de TypeScript en tests de Playwright

Property 'X' does not exist on type 'Y'

Estás llamando a un método o accediendo a una propiedad que no existe. O es un error de tipeo, o necesitas importar el tipo correcto.

Object is possibly 'null'

TypeScript sabe que un método podría devolver null. Verifica el valor de retorno:

const text = await page.getByRole('heading').textContent();
// TypeScript: text podría ser null

if (text !== null) {
  expect(text).toBe('My Travel Items');
}
// O usá la aserción non-null si estás seguro:
expect(text!).toBe('My Travel Items');

Argument of type 'string' is not assignable to parameter of type 'number'

Tipo incorrecto pasado a una función. Verifica la firma de la función y corrige el argumento.

'await' has no effect on the type of this expression

Estás usando await en algo que no es una Promise. Elimina el await o verifica por qué el método no devuelve una Promise.

JavaScript vs TypeScript: la decisión práctica

Si empiezas desde cero: TypeScript. La curva de aprendizaje sobre JavaScript puro es mínima, y los beneficios (autocompletado, detección de errores) se recuperan de inmediato.

Si tienes un proyecto Playwright existente en JavaScript: considera migrar. Renombra .js a .ts, añade un tsconfig.json, corrige los errores de tipo. Para un proyecto típico esto lleva unas pocas horas.

Si tu equipo usa JavaScript: los archivos TypeScript se compilan a JavaScript, así que coexisten. Puedes migrar archivo por archivo.

Preguntas frecuentes

¿Necesito compilar TypeScript antes de correr los tests?

No. Playwright maneja la compilación internamente. Ejecutas npx playwright test exactamente como antes. Playwright compila TypeScript al vuelo usando su transpilador incorporado.

TypeScript me da errores, pero los tests igualmente corren. ¿Por qué?

Los errores de TypeScript son advertencias del verificador de tipos, no errores en tiempo de ejecución. El runner de tests compila el código independientemente de los errores de tipo. El punto de corregir los errores de TypeScript es detectar bugs antes de que se conviertan en fallos en tiempo de ejecución, no hacer que los tests corran.

¿Necesito entender todos los tipos de TypeScript que usa Playwright?

No. Necesitas importar Page, Locator y APIRequestContext al escribir page objects tipados. El resto lo irás encontrando y consultando según lo necesites.

¿Es TypeScript significativamente más difícil de aprender que JavaScript para tests?

Las adiciones relevantes para código de tests son: anotaciones de tipo en parámetros de funciones, interfaces para datos de prueba, e importar tipos de Playwright. Si ya sabes JavaScript, aprender esto lleva unos días, no semanas.

→ See also: Git y GitHub para QA Engineers: Un Tutorial Sin Relleno | Page Object Model en Playwright: De Caótico a Mantenible