In a React Native TypeScript project using @react-native/jest-preset, how are .tsx tests compiled, and why don't type errors fail the run?
answer
- one transform for js, ts, tsx
- babel-jest reads babel.config.js
- types stripped, never checked
- tsc runs as its own step
- @types/jest for editor types
basics
~10 sThe preset sends .js, .ts and .tsx files to babel-jest, which applies the project's Babel config with @react-native/babel-preset; its TypeScript plugin strips types without checking them, so type errors need a separate tsc run.
solid answer
~40 sThe preset's `transform` maps `^.+\.(js|ts|tsx)$` to `babel-jest`. `babel-jest` reads the project's `babel.config.js`, which in a React Native app uses `@react-native/babel-preset`, and that preset applies `@babel/plugin-transform-typescript` to `.ts` and `.tsx` files. The plugin removes type annotations file by file and never runs the type checker, so a test that passes a string where a number is typed still executes. React Native's docs recommend using the TypeScript compiler only for type checking, so the pipeline runs `tsc` as its own step, in CI and in the editor. Babel handling one file at a time also means TypeScript-only constructs that need cross-file type information are the documented Babel caveats.
code
javascript · 4 lines// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
};go deeper
Recall that the preset compiles TypeScript tests with babel-jest and that type checking is a separate tsc step.
Explain the chain from the preset's transform to babel.config.js and @react-native/babel-preset's TypeScript plugin, and why stripping is not checking.
Diagnose the consequences: aliases that need module-resolver, Babel's per-file caveats, and CI that must run tsc so a green test suite is not mistaken for type safety.
Decide between one compiler shared by Metro and Jest plus a separate type-check job, and a slower type-checking test transform, based on feedback speed and consistency.
## The compile path for a TypeScript test When Jest loads `CalendarScreen.test.tsx`, it asks its **transform** configuration which compiler handles the file. The React Native preset sets one rule for code: ```js transform: { '^.+\\.(js|ts|tsx)$': 'babel-jest', // plus an asset transformer for images and video } ``` So the chain is: 1. Jest hands the file to **`babel-jest`**. 2. `babel-jest` loads the project's Babel configuration, normally `babel.config.js` with `presets: ['module:@react-native/babel-preset']`. 3. `@react-native/babel-preset` applies **`@babel/plugin-transform-typescript`** to files ending in `.ts` and, with JSX enabled, to files ending in `.tsx`. 4. The plugin deletes type annotations, interfaces and type-only imports, and the rest of the preset compiles JSX and modern syntax. 5. Jest runs the resulting JavaScript. This is the same Babel preset Metro uses when it bundles the app, which keeps test and app compilation close to each other. ## Why type errors do not fail the tests Babel is a **syntax transformer**. The TypeScript plugin understands TypeScript's grammar well enough to remove it, but it does not build a type graph or report type errors. That has two effects: - A test that passes `count: '3'` to a prop typed `number` compiles and runs; it fails only if the runtime behaviour breaks an assertion. - A `.tsx` file with an obvious type error still executes, so a green Jest run says nothing about type safety. React Native's TypeScript guide states the intended split directly: TypeScript sources are transformed by Babel, and the TypeScript compiler is recommended only for type checking. In practice teams run `tsc` (usually with `--noEmit`) as its own CI step, next to Jest. ## Babel's per-file limits Because Babel compiles **one file at a time** without type information, a few TypeScript constructs behave differently from `tsc` output or are unsupported. Babel's TypeScript plugin documents these caveats; the React Native docs link to them for projects porting code written for `tsc`. The practical rule: code that relies on type information from other files to emit JavaScript is the risky category. ## Comparing the options | Approach | Type checks during tests | Speed | Matches Metro's compile | |---|---|---|---| | `babel-jest` with `@react-native/babel-preset` (preset default) | No | Fast | Yes, same Babel preset | | A TypeScript-compiler-based Jest transformer such as `ts-jest` | Can report type errors | Slower | No, a different compiler | | `babel-jest` plus a separate `tsc --noEmit` step | Yes, in its own step | Fast tests, one extra job | Yes | Most React Native projects take the third row: the default preset for tests plus a separate type-check job. ## Things that follow from the Babel path - **Path aliases.** `tsconfig.json` `paths` are read by `tsc`, not by Babel. React Native's docs configure `babel-plugin-module-resolver` in `babel.config.js` for aliases, and because `babel-jest` reads the same file, the aliases work in tests too. - **Jest types.** Test files use `describe`, `it` and `expect` as globals. React Native's TypeScript setup installs `@types/jest` so the editor and `tsc` know those globals; Babel does not need them. - **Other extensions.** The preset's code transform matches `.js`, `.ts` and `.tsx`; if a project uses another source extension, add a transform rule for it. - **Flow in dependencies.** React Native's own source is written with Flow types; `@react-native/babel-preset` also strips Flow, which is why the same `babel-jest` rule handles both your TypeScript and React Native's Flow files once they are allowlisted for transforming. - **Consistency with Metro.** If you add a Babel plugin for the app (for example a library's plugin that rewrites code), it lives in `babel.config.js` and applies in tests automatically, which is usually what you want. ## The interview answer in one line The preset compiles TypeScript with Babel through `babel-jest` and the React Native Babel preset, which strips types without checking them; type safety comes from a separate `tsc` run, not from Jest.
- Imports using a tsconfig path alias work in the editor but fail in Jest; how do you fix it in a React Native project?Babel ignores `tsconfig.json` `paths`. Configure the same aliases with `babel-plugin-module-resolver` in `babel.config.js`, as React Native's TypeScript guide shows; `babel-jest` reads that file, so tests and the Metro bundle resolve them alike. Mapping the alias with Jest's `moduleNameMapper` also works but duplicates the list.
- When would a team switch the TypeScript transform to a compiler-based transformer instead of babel-jest?When it wants type errors reported in the test run itself and accepts slower runs, or relies on TypeScript features Babel handles differently. The trade-off is compiling tests with a different tool from Metro, so most React Native projects keep `babel-jest` and add a `tsc` step.
saying these in an interview costs you the question
- babel-jest fails the test when a prop has the wrong type
- Jest reads tsconfig.json paths without any extra configuration
- The preset uses the TypeScript compiler to build .tsx tests
- A green Jest run proves the code type-checks
- Metro and Jest compile TypeScript with different Babel presets by default