Modern E2E frameworks are TypeScript-first: Playwright generates TS projects by default and Cypress supports TS with minimal setup. Types make locators, fixtures, custom commands, and API responses safe and autocompleted.
In Playwright, test functions receive typed fixtures ({ page }: PlaywrightTestArgs) and Page/Locator methods are fully typed, so page.getByRole('button', { name }) autocompletes options and flags typos. Cypress custom commands need a type declaration (declare global { namespace Cypress { interface Chainable { login(u: string): void } } }) so cy.login autocompletes. Typing API responses (await request.get(url) then as ApiUser, or a generic helper) catches field-name mistakes in assertions. A tsconfig extending the framework's config plus strict mode gives the best experience. TypeScript is why these frameworks feel safe to refactor at scale.
A Cypress custom command typed via declare global { namespace Cypress { interface Chainable { login(user: string, pass: string): Chainable } } } gives cy.login autocomplete and errors if a test omits an argument.
After adding a Cypress custom command cy.login, TypeScript complains 'Property login does not exist on type Chainable'. What's missing?