TypeScript

Cypress ships with official type declarations for TypeScript. This allows you to write your tests in TypeScript.

Install TypeScript

You'll need to have TypeScript 3.4+ installed within your project to have TypeScript support within Cypress.

With npm

npm install --save-dev typescript

With yarn

yarn add --dev typescript

Set up your dev environment

Please refer to your code editor in TypeScript's Editor Support doc and follow the instructions for your IDE to get TypeScript support and intelligent code completion configured in your developer environment before continuing. TypeScript support is built in for Visual Studio Code, Visual Studio, and WebStorm - all other editors require extra setup.

Configure tsconfig.json

We recommend the following configuration in a tsconfig.json inside your cypress folder.

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es5", "dom"],
    "types": ["cypress"]
  },
  "include": ["**/*.ts"]
}

The "types" will tell the TypeScript compiler to only include type definitions from Cypress. This will address instances where the project also uses @types/chai or @types/jquery. Since Chai and jQuery are namespaces (globals), incompatible versions will cause the package manager (yarn or npm) to nest and include multiple definitions and cause conflicts.

Types for custom commands

When adding custom commands to the cy object, you can manually add their types to avoid TypeScript errors.

For example if you add the command cy.dataCy into your supportFile like this:

// cypress/support/index.ts
Cypress.Commands.add('dataCy', (value) => {
  return cy.get(`[data-cy=${value}]`)
})

Then you can add the dataCy command to the global Cypress Chainable interface (so called because commands are chained together) to your cypress/support/index.ts file.

// in cypress/support/index.ts
// load type definitions that come with Cypress module
/// <reference types="cypress" />

declare global {
  namespace Cypress {
    interface Chainable {
      /**
       * Custom command to select DOM element by data-cy attribute.
       * @example cy.dataCy('greeting')
       */
      dataCy(value: string): Chainable<Element>
    }
  }
}

In your specs, you can now use the custom command as expected

// from your cypress/e2e/spec..cy.ts
it('works', () => {
  cy.visit('/')
  // IntelliSense and TS compiler should
  // not complain about unknown method
  cy.dataCy('greeting')
})
// from your cypress/e2e/spec..cy.ts
it('works', () => {
  cy.mount(<MyComponent />)
  // IntelliSense and TS compiler should
  // not complain about unknown method
  cy.dataCy('greeting')
})

Adding child or dual commands

When you add a custom command with prevSubject, Cypress will infer the subject type automatically based on the specified prevSubject.

// in cypress/support/index.ts
// load type definitions that come with Cypress module
/// <reference types="cypress" />

declare global {
  namespace Cypress {
    interface Chainable {
      /**
       * Custom command to type a few random words into input elements
       * @param count=3
       * @example cy.get('input').typeRandomWords()
       */
      typeRandomWords(
        count?: number,
        options?: Partial<TypeOptions>
      ): Chainable<Element>
    }
  }
}
// cypress/support/index.ts
Cypress.Commands.add('typeRandomWords', { prevSubject: 'element' }, (
  subject /* :JQuery<HTMLElement> */,
  count = 3,
  options?
) => {
  return cy.wrap(subject).type(generateRandomWords(count), options)
})

Overwriting child or dual commands

When overwriting either built-in or custom commands which make use of prevSubject, you must specify generic parameters to help the type-checker to understand the type of the prevSubject.

interface TypeOptions extends Cypress.TypeOptions {
  sensitive: boolean
}

Cypress.Commands.overwrite<'type', 'element'>(
  'type',
  (originalFn, element, text, options?: Partial<TypeOptions>) => {
    if (options && options.sensitive) {
      // turn off original log
      options.log = false
      // create our own log with masked message
      Cypress.log({
        $el: element,
        name: 'type',
        message: '*'.repeat(text.length),
      })
    }

    return originalFn(element, text, options)
  }
)

As you can see there are generic parameters <'type', 'element'> are used:

  1. The first parameter is the command name, equal to first parameter passed to Cypress.Commands.overwrite.
  2. The second parameter is the type of the prevSubject that is used by the original command. Possible values:
    • 'element' infers it as JQuery<HTMLElement>
    • 'window' infers it as Window
    • 'document' infers it as Document
    • 'optional' infers it as unknown

Examples:

Types for custom assertions

If you extend Cypress assertions, you can extend the assertion types to make the TypeScript compiler understand the new methods. See the Recipe: Adding Chai Assertions for instructions.

Types for plugins

You can utilize Cypress's type declarations in your plugins file by annotating it like the following:

// cypress/plugins/index.ts

/// <reference types="cypress" />

/**
 * @type {Cypress.PluginConfig}
 */
module.exports = (on, config) => {}

Clashing types with Jest

If you are using both Jest and Cypress in the same project, the TypeScript types registered globally by the two test runners can clash. For example, both Jest and Cypress provide the clashing types for the describe and it functions. Both Jest and Expect (bundled inside Cypress) provide the clashing types for the expect assertion, etc. There are two solutions to disentangle the types:

  1. Configure a separate tsconfig.json for E2E tests. See our example cypress-io/cypress-and-jest-typescript-example repo.
  2. Remove Cypress global variables by using NPM package local-cypress. Read the blog post How to Avoid Using Global Cypress Variables for details.

History

VersionChanges
5.0.0Raised minimum required TypeScript version from 2.9+ to 3.4+
4.4.0Added support for TypeScript without needing your own transpilation through preprocessors.

See also