# Types and imports

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

Source: https://rehearsal.dev/retest/typescript

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

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

```ts filename="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

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.

```ts
// A type error: "A macOS app takes no swipe. Use scroll()."
await desk.swipe('up')
```

## 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.

```json filename="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

- 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

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.

```json filename="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

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

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

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.

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