Types and imports

Let TypeScript check your app names, set up the tests tsconfig, and write imports the way Retest loads them.

Retest runs .ts files as they are, with no build step. It removes the types with the transform built into Node.js. It does not check types, so run tsc for that.

Typed names Link to Typed names

The declare module block in the config registers its type once, for every file in the same TypeScript program.

retest.config.ts
declare module '@rehearsal-labs/retest' {  interface Register {    config: typeof config  }}

The type check then knows your names:

  • apps takes only the config's app names, and a test can use only the apps it declares.
  • tags, state, secret() and getByTestId() take only the names the config lists. A config that lists no tags, states or testIds accepts any string for them.
  • locks takes only the config's locks.
  • Without a registered config, a test cannot declare apps or state, and the error says how to register.

A registration applies to the whole program, so keep a registered project in a tsconfig of its own. Retest checks the same names again when it loads the tests, for code with no types.

Typed capabilities Link to Typed capabilities

What a page can do depends on what it is, and the types say so:

  • tap() exists only on an app whose every target has a touch screen.
  • An iOS simulator or macOS app has no goto, select, check or uncheck. Its elements take tap() on iOS and click() on macOS.
  • An Electron app has no goto: its page is the first window the app opens.
  • A judge name with a typo, or a screenshot for a judge that reads only text, fails to compile.
TypeScript
// A type error: "A macOS app takes no swipe. Use scroll()."await desk.swipe('up')

The tests tsconfig Link to The tests tsconfig

retest init writes tests/tsconfig.json. It extends your project's own tsconfig.json when there is one, and its options accept what Retest loads and refuse what it does not.

tests/tsconfig.json
{  "compilerOptions": {    "target": "es2024",    "module": "esnext",    "moduleResolution": "bundler",    "types": ["node"],    "strict": true,    "noEmit": true,    "allowImportingTsExtensions": true,    "verbatimModuleSyntax": true,    "erasableSyntaxOnly": false,    "noUncheckedIndexedAccess": true,    "skipLibCheck": true  },  "include": ["../retest.config.ts", "**/*"]}

The typecheck:e2e script init adds runs tsc -p tests/tsconfig.json.

Imports Link to Imports

  • Test files, helpers and the config are ES modules, in .ts, .mts, .js or .mjs files. Set "type": "module" in package.json.
  • A relative import works with or without its extension. ./helper finds ./helper.ts, then ./helper.js, ./helper.mts and ./helper.mjs, then the folder's index.ts or index.js.
  • ./helper.js loads ./helper.ts when that file exists, as tsc reads it.
  • Enums, namespaces with values and parameter properties load too.

Path aliases Link to Path aliases

Retest reads the paths of the tsconfig.json nearest the importing file. It follows extends, allows comments and trailing commas, and reads paths from baseUrl when one is set.

tsconfig.json
{  "compilerOptions": {    "paths": { "@/*": ["./src/*"] }  }}

An alias tries its targets in order, each with the extensions above. TypeScript 7 removed baseUrl, and paths work the same without it. An import that cannot be found names the tsconfig.json that governed it.

What does not load Link to What does not load

These fail as their file loads, with a message that names the construct and the file:

  • CommonJS test files and configs: .cts files, require(), module.exports and __dirname. Write import and export.
  • JSX, in .tsx and .jsx files or inside a .ts file.
  • Decorators, which Node.js cannot run.
  • A type imported without type. Write import type { Title } from './titles.ts'.

TypeScript 6 and 7 Link to TypeScript 6 and 7

Retest's types are checked with both TypeScript 6 and TypeScript 7, and so is the tsconfig init writes. Use whichever your project has.

Source maps Link to Source maps

Types are removed in place, so every line and column stays where it was. A file with an enum, a namespace with values or a parameter property is transformed instead, with a source map. Either way, a failure names its TypeScript line and column.

Retest has run Node's TypeScript transform on Node.js 24.12, which is why it asks for 24.12 or later.