<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Unit-Testing on RockB</title><link>https://baeseokjae.github.io/tags/unit-testing/</link><description>Recent content in Unit-Testing on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 18 May 2026 09:04:57 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/unit-testing/index.xml" rel="self" type="application/rss+xml"/><item><title>ts-jest TypeScript Unit Testing Jest Integration Guide 2026</title><link>https://baeseokjae.github.io/posts/ts-jest-typescript-unit-testing-jest-integration-guide-2026/</link><pubDate>Mon, 18 May 2026 09:04:57 +0000</pubDate><guid>https://baeseokjae.github.io/posts/ts-jest-typescript-unit-testing-jest-integration-guide-2026/</guid><description>Complete ts-jest setup guide for Jest 30 and TypeScript 5 in 2026: installation, configuration, ESM, monorepo, and common errors fixed.</description><content:encoded><![CDATA[<p>ts-jest is the official TypeScript preprocessor for Jest, transforming <code>.ts</code> and <code>.tsx</code> source files into JavaScript that Jest can execute. With 22.7 million weekly npm downloads in 2026, it remains the standard integration layer for TypeScript projects using Jest.</p>
<h2 id="what-is-ts-jest-and-why-it-still-matters-in-2026">What Is ts-jest and Why It Still Matters in 2026</h2>
<p>ts-jest is a TypeScript preprocessor for Jest that compiles <code>.ts</code> and <code>.tsx</code> files at test runtime using the TypeScript compiler (<code>tsc</code>). Unlike Babel-based approaches, ts-jest performs real TypeScript type checking during test execution, giving you full type safety without a separate compilation step. As of 2026, ts-jest v29.4.9 supports Jest 29–30 and TypeScript 5.x, with 22.7 million weekly npm downloads and 7,077+ GitHub stars. The package has 3,729 direct dependents on npm, making it deeply embedded in the JavaScript ecosystem.</p>
<p>The key reason ts-jest remains relevant in 2026 despite competitors like Vitest is its ecosystem compatibility. React Native projects require Jest&rsquo;s jsdom environment, and large legacy codebases with millions of lines of Jest tests cannot migrate overnight. ts-jest also provides the closest semantics to production TypeScript compilation because it uses the actual TypeScript compiler — meaning your test types match your production types exactly, with no transpile shortcuts. If you&rsquo;re running Jest 30 on a TypeScript 5 project and need type-safe tests with minimal setup, ts-jest is still the most direct path.</p>
<h3 id="how-ts-jest-differs-from-swcjest-and-babel">How ts-jest Differs From @swc/jest and Babel</h3>
<p>ts-jest uses the TypeScript compiler (<code>tsc</code>) to transform files, which means full type information is available. <code>@swc/jest</code> strips types without checking them (3-5x faster transforms, zero type safety). Babel with <code>@babel/preset-typescript</code> also strips types. For CI pipelines where correctness matters more than raw speed, ts-jest remains the default choice.</p>
<h2 id="installing-ts-jest-with-jest-30-and-typescript-5">Installing ts-jest with Jest 30 and TypeScript 5</h2>
<p>Installing ts-jest correctly with Jest 30 and TypeScript 5 requires three packages: <code>jest</code>, <code>ts-jest</code>, and <code>@types/jest</code>. As of 2026, the compatible version combination is Jest ≥30.0.0, ts-jest ≥29.4.0, and TypeScript ≥5.0.0. Run the following to install the complete set of dependencies in one command, ensuring peer dependency alignment across the entire testing stack.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install --save-dev jest ts-jest @types/jest typescript
</span></span></code></pre></div><p>For a TypeScript project starting from scratch, also add a <code>tsconfig.json</code> if one doesn&rsquo;t exist:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npx tsc --init
</span></span></code></pre></div><p>Verify that your <code>tsconfig.json</code> includes <code>&quot;module&quot;: &quot;CommonJS&quot;</code> (or <code>&quot;NodeNext&quot;</code> for ESM — covered separately below) and <code>&quot;strict&quot;: true</code>. ts-jest reads your project&rsquo;s <code>tsconfig.json</code> by default, so mismatches between your tsconfig and jest config are a common source of errors.</p>
<p>After installation, confirm the version alignment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npx ts-jest --version
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Expected: ts-jest/29.x.x with jest 30.x.x</span>
</span></span></code></pre></div><h3 id="typescript-5-compatibility-notes">TypeScript 5 Compatibility Notes</h3>
<p>TypeScript 5 introduced decorator changes (<code>experimentalDecorators</code> now defaults to <code>false</code>). If your codebase uses decorators (NestJS, TypeORM), add <code>&quot;experimentalDecorators&quot;: true</code> to your tsconfig before running ts-jest. Also, TypeScript 5&rsquo;s stricter module resolution under <code>&quot;moduleResolution&quot;: &quot;bundler&quot;</code> is not yet fully supported by ts-jest — stick with <code>&quot;moduleResolution&quot;: &quot;node16&quot;</code> or <code>&quot;node&quot;</code> for test environments.</p>
<h2 id="configuring-ts-jest-complete-jestconfigts-reference">Configuring ts-jest: Complete jest.config.ts Reference</h2>
<p>Configuring ts-jest requires a <code>jest.config.ts</code> file (or <code>.js</code>/<code>.mjs</code>) at your project root. Jest 30 natively supports <code>jest.config.ts</code>, eliminating the need for Babel transpilation during test initialization — a significant improvement over Jest 28. The configuration below is a production-ready starting point for a Node.js TypeScript project with all major ts-jest options documented inline. The most important fields are the <code>transform</code> entry (which wires ts-jest as the TypeScript preprocessor), <code>moduleNameMapper</code> (which resolves path aliases like <code>@/</code>), and <code>diagnostics</code> (which controls whether type errors fail the test run). Getting these three fields right eliminates 90% of ts-jest configuration issues seen in 2026 projects.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#66d9ef">type</span> { <span style="color:#a6e22e">Config</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;jest&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span>: <span style="color:#66d9ef">Config</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">preset</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ts-jest&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">testEnvironment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;node&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">roots</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;&lt;rootDir&gt;/src&#39;</span>],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">testMatch</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;**/__tests__/**/*.ts&#39;</span>, <span style="color:#e6db74">&#39;**/*.spec.ts&#39;</span>, <span style="color:#e6db74">&#39;**/*.test.ts&#39;</span>],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">tsconfig</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;tsconfig.json&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">true</span>,     <span style="color:#75715e">// fastest option — see next section
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#a6e22e">diagnostics</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ignoreCodes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;TS151001&#39;</span>] <span style="color:#75715e">// suppress known ts-jest diagnostic
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }
</span></span><span style="display:flex;"><span>    }]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">moduleNameMapper</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^@/(.*)$&#39;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/src/$1&#39;</span>  <span style="color:#75715e">// resolve @ path aliases
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">collectCoverageFrom</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;src/**/*.ts&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;!src/**/*.d.ts&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;!src/**/index.ts&#39;</span>
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">config</span>;
</span></span></code></pre></div><h3 id="key-configuration-fields-explained">Key Configuration Fields Explained</h3>
<ul>
<li><strong><code>preset: 'ts-jest'</code></strong> — shorthand that sets <code>transform</code>, <code>testMatch</code>, and <code>moduleFileExtensions</code> for TypeScript projects</li>
<li><strong><code>testEnvironment: 'node'</code></strong> — use <code>'jsdom'</code> for React/browser projects (requires <code>jest-environment-jsdom</code>)</li>
<li><strong><code>moduleNameMapper</code></strong> — critical for projects using TypeScript path aliases (<code>@/components</code> → <code>src/components</code>); without this, ts-jest will throw &ldquo;Cannot find module&rdquo; errors</li>
<li><strong><code>diagnostics</code></strong> — controls whether type errors fail tests; set <code>diagnostics: false</code> to disable type checking in ts-jest (useful when running <code>tsc --noEmit</code> separately in CI)</li>
</ul>
<h2 id="isolatedmodules--the-fastest-path-to-typescript-testing">isolatedModules — The Fastest Path to TypeScript Testing</h2>
<p><code>isolatedModules: true</code> is the single most impactful performance optimization available to ts-jest users, reducing transform time by 30-60% in typical projects. When enabled, ts-jest transforms each file independently without cross-file type information — identical to TypeScript&rsquo;s <code>--isolatedModules</code> flag. The trade-off is that some TypeScript features requiring cross-file analysis (const enums, namespace imports, ambient declarations) will throw errors at transform time.</p>
<p>Setting <code>isolatedModules: true</code> in ts-jest&rsquo;s transform options tells the compiler to skip the type-checking phase entirely during test runs. This makes ts-jest behave more like Babel or @swc/jest in terms of speed, while retaining TypeScript syntax parsing. For a 200-test suite, this can cut runtime from 12 seconds down to 5-7 seconds — a meaningful improvement in local development feedback loops.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  }]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="when-not-to-use-isolatedmodules">When NOT to Use isolatedModules</h3>
<p>Avoid <code>isolatedModules: true</code> if your codebase relies on:</p>
<ul>
<li><code>const enum</code> declarations (they require cross-file compilation)</li>
<li><code>.d.ts</code> ambient modules with re-exported types used in test assertions</li>
<li>TypeScript <code>namespace</code> merging patterns</li>
</ul>
<p>In these cases, keep <code>isolatedModules: false</code> (the default) and accept the slower compile time. For the majority of modern TypeScript codebases following ESM module conventions, <code>isolatedModules: true</code> is safe and strongly recommended.</p>
<h2 id="esm-support-with-ts-jest-experimental-but-production-ready">ESM Support with ts-jest (Experimental but Production-Ready)</h2>
<p>ts-jest&rsquo;s ESM support in 2026 is experimental in name but production-viable in practice for Node.js projects. The configuration requires two environment-level changes alongside ts-jest options: setting <code>NODE_OPTIONS=--experimental-vm-modules</code> and using <code>extensionsToTreatAsEsm</code> in jest config. This setup enables native ES module syntax (<code>import</code>/<code>export</code>) in test files without CommonJS transformation.</p>
<p>ESM with ts-jest is necessary when your dependencies ship ESM-only packages (a growing list in 2026, including <code>node-fetch</code>, <code>chalk</code>, and many utility libraries). Without ESM mode, ts-jest will fail on these imports with <code>SyntaxError: Cannot use import statement in a module</code>.</p>
<p><strong>Step 1</strong> — Update <code>package.json</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;module&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;scripts&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;test&#34;</span>: <span style="color:#e6db74">&#34;NODE_OPTIONS=--experimental-vm-modules jest&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Step 2</strong> — Update <code>jest.config.ts</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span>: <span style="color:#66d9ef">Config</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">preset</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ts-jest/presets/default-esm&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">testEnvironment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;node&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">extensionsToTreatAsEsm</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;.ts&#39;</span>],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">useESM</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    }]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">moduleNameMapper</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^(\\.{1,2}/.*)\\.js$&#39;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;$1&#39;</span>  <span style="color:#75715e">// strip .js extensions for ESM resolution
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p><strong>Step 3</strong> — Update <code>tsconfig.json</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;compilerOptions&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;module&#34;</span>: <span style="color:#e6db74">&#34;NodeNext&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;moduleResolution&#34;</span>: <span style="color:#e6db74">&#34;NodeNext&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="esm-gotchas-in-2026">ESM Gotchas in 2026</h3>
<p>The <code>moduleNameMapper</code> stripping <code>.js</code> extensions is critical — when TypeScript compiles ESM, it emits <code>import './foo.js'</code> even for <code>.ts</code> source files. Jest&rsquo;s resolver can&rsquo;t find <code>.ts</code> files at <code>.js</code> paths without this mapping. Also note: <code>jest.config.ts</code> itself must use <code>import</code>/<code>export</code> syntax when <code>&quot;type&quot;: &quot;module&quot;</code> is set in <code>package.json</code>.</p>
<h2 id="setting-up-ts-jest-for-react-typescript-projects">Setting Up ts-jest for React TypeScript Projects</h2>
<p>Setting up ts-jest for React TypeScript projects requires the jsdom test environment and JSX transform configuration in addition to the standard ts-jest setup. React 18+ uses the automatic JSX runtime, which changes how <code>@jsx</code> directives work in TypeScript. Failing to configure this correctly produces <code>React is not defined</code> errors even when React is installed.</p>
<p>Start by installing the additional dependencies:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install --save-dev jest-environment-jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
</span></span></code></pre></div><p>Then configure <code>jest.config.ts</code> for React:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#66d9ef">type</span> { <span style="color:#a6e22e">Config</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;jest&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span>: <span style="color:#66d9ef">Config</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">preset</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ts-jest&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">testEnvironment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;jsdom&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">setupFilesAfterFramework</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;&lt;rootDir&gt;/src/setupTests.ts&#39;</span>],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">tsconfig</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">jsx</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;react-jsx&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">jsxImportSource</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;react&#39;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    }]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">moduleNameMapper</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;\\.(css|less|scss|sass)$&#39;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/__mocks__/styleMock.js&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;\\.(jpg|jpeg|png|gif|svg)$&#39;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/__mocks__/fileMock.js&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">config</span>;
</span></span></code></pre></div><p>Create a setup file for jest-dom matchers and static asset stubs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// src/setupTests.ts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;@testing-library/jest-dom&#39;</span>;
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// __mocks__/styleMock.js
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">module</span>.<span style="color:#a6e22e">exports</span> <span style="color:#f92672">=</span> {};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// __mocks__/fileMock.js
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">module</span>.<span style="color:#a6e22e">exports</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;test-file-stub&#39;</span>;
</span></span></code></pre></div><h3 id="testing-a-react-component-with-ts-jest">Testing a React Component with ts-jest</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// src/components/Button.test.tsx
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">render</span>, <span style="color:#a6e22e">screen</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@testing-library/react&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">userEvent</span> <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@testing-library/user-event&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Button</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;./Button&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">describe</span>(<span style="color:#e6db74">&#39;Button&#39;</span>, () <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">it</span>(<span style="color:#e6db74">&#39;calls onClick when clicked&#39;</span>, <span style="color:#66d9ef">async</span> () <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">handleClick</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">jest</span>.<span style="color:#a6e22e">fn</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">render</span>(&lt;<span style="color:#f92672">Button</span> <span style="color:#a6e22e">onClick</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">handleClick</span>}&gt;<span style="color:#a6e22e">Click</span> <span style="color:#a6e22e">me</span>&lt;/<span style="color:#f92672">Button</span>&gt;);
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">userEvent</span>.<span style="color:#a6e22e">click</span>(<span style="color:#a6e22e">screen</span>.<span style="color:#a6e22e">getByRole</span>(<span style="color:#e6db74">&#39;button&#39;</span>));
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expect</span>(<span style="color:#a6e22e">handleClick</span>).<span style="color:#a6e22e">toHaveBeenCalledTimes</span>(<span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="ts-jest-in-monorepo-environments-with-jest-projects-api">ts-jest in Monorepo Environments with Jest Projects API</h2>
<p>ts-jest in monorepo environments works best with Jest 30&rsquo;s <code>projects</code> API, which lets a single <code>jest</code> command run tests across multiple packages with package-specific ts-jest configurations. This approach eliminates the need for separate <code>jest</code> invocations per package and centralizes CI test orchestration. Jest 30 improvements to project-level configurations can reduce CI/CD pipeline duration by 10-15 minutes in large monorepos compared to Jest 28 approaches.</p>
<p>The monorepo structure uses a root-level <code>jest.config.ts</code> that references per-package configurations:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 400 169"
      >
      <g transform='translate(8,16)'>
<path d='M 88,48 L 96,32' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='116' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='132' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='148' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='32' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='32' y='100' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='32' y='148' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='148' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='56' y='148' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='84' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='64' y='100' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='64' y='116' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='132' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='64' y='148' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='72' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='100' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='72' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='148' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='80' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='100' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='80' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='148' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='88' y='148' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='96' y='84' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='116' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='96' y='132' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='148' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='104' y='116' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='132' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='104' y='148' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='84' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='116' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='132' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='148' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='132' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='68' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='128' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='116' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='128' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='136' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='136' y='84' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='116' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='136' y='132' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='148' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='144' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='84' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='132' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='148' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='152' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='152' y='84' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='116' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='152' y='132' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='68' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='160' y='84' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='160' y='116' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='160' y='132' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='160' y='148' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='168' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='168' y='84' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='168' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='168' y='132' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='168' y='148' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='176' y='68' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='176' y='116' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='132' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='184' y='68' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='184' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='184' y='116' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='184' y='132' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='192' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='192' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='192' y='132' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='200' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='200' y='116' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='224' y='68' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='224' y='116' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='224' y='148' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='240' y='68' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='240' y='116' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='240' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='68' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='148' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='256' y='116' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='256' y='148' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='68' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='264' y='116' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='264' y='148' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='272' y='116' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='272' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='280' y='68' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='280' y='116' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='148' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='288' y='116' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='296' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='296' y='148' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='304' y='116' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='304' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='68' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='312' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='148' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='320' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='116' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='320' y='148' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='328' y='148' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='336' y='116' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='336' y='148' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='344' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='344' y='148' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='352' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='116' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='148' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='360' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='360' y='116' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='368' y='68' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='368' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='376' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='376' y='116' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='384' y='68' fill='currentColor' style='font-size:1em'>g</text>
</g>

    </svg>
  
</div>
<p><strong>Root <code>jest.config.ts</code>:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#66d9ef">type</span> { <span style="color:#a6e22e">Config</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;jest&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span>: <span style="color:#66d9ef">Config</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">projects</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;&lt;rootDir&gt;/packages/api/jest.config.ts&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;&lt;rootDir&gt;/packages/ui/jest.config.ts&#39;</span>
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">coverageDirectory</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/coverage&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">collectCoverageFrom</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;packages/*/src/**/*.ts&#39;</span>]
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">config</span>;
</span></span></code></pre></div><p><strong>Package-level <code>packages/api/jest.config.ts</code>:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#66d9ef">type</span> { <span style="color:#a6e22e">Config</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;jest&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">config</span>: <span style="color:#66d9ef">Config</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">displayName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;api&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">preset</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ts-jest&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">testEnvironment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;node&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">rootDir</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;.&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">tsconfig</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/tsconfig.json&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    }]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">config</span>;
</span></span></code></pre></div><h3 id="shared-tsconfig-for-monorepos">Shared tsconfig for Monorepos</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#75715e">// tsconfig.base.json
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;compilerOptions&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;strict&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;module&#34;</span>: <span style="color:#e6db74">&#34;CommonJS&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;moduleResolution&#34;</span>: <span style="color:#e6db74">&#34;node&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;esModuleInterop&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;declaration&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;skipLibCheck&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// packages/api/tsconfig.json
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;extends&#34;</span>: <span style="color:#e6db74">&#34;../../tsconfig.base.json&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;compilerOptions&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;outDir&#34;</span>: <span style="color:#e6db74">&#34;./dist&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;rootDir&#34;</span>: <span style="color:#e6db74">&#34;./src&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="ts-jest-vs-swcjest-vs-vitest-choosing-your-stack-in-2026">ts-jest vs @swc/jest vs Vitest: Choosing Your Stack in 2026</h2>
<p>ts-jest, @swc/jest, and Vitest represent three distinct philosophies for TypeScript testing in 2026, each with clear trade-offs around speed, type safety, and compatibility. Vitest runs 5-28x faster than Jest with ts-jest across benchmark scenarios, and for a 200-test suite this translates to 2-4 seconds (Vitest) versus 8-12 seconds (Jest+ts-jest). Despite this speed gap, ts-jest remains the correct choice for specific contexts: React Native projects, teams with deep Jest expertise, and existing codebases with thousands of Jest tests.</p>
<table>
  <thead>
      <tr>
          <th>Factor</th>
          <th>ts-jest</th>
          <th>@swc/jest</th>
          <th>Vitest</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Transform speed</td>
          <td>Slowest (uses tsc)</td>
          <td>Fast (Rust compiler)</td>
          <td>Fastest (Vite-native)</td>
      </tr>
      <tr>
          <td>Type checking</td>
          <td>Full (optional)</td>
          <td>None (strips types)</td>
          <td>None (strips types)</td>
      </tr>
      <tr>
          <td>Jest API compat</td>
          <td>100%</td>
          <td>100%</td>
          <td>~95% (minor gaps)</td>
      </tr>
      <tr>
          <td>React Native</td>
          <td>Yes</td>
          <td>Yes</td>
          <td>No (limited)</td>
      </tr>
      <tr>
          <td>ESM support</td>
          <td>Experimental</td>
          <td>Good</td>
          <td>Native</td>
      </tr>
      <tr>
          <td>Config complexity</td>
          <td>Medium</td>
          <td>Low</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Migration effort</td>
          <td>Baseline</td>
          <td>Low from ts-jest</td>
          <td>Medium</td>
      </tr>
  </tbody>
</table>
<h3 id="when-to-choose-each-option">When to Choose Each Option</h3>
<p><strong>Choose ts-jest</strong> when: you&rsquo;re on React Native, your team uses <code>jest.mock()</code> extensively with type-safe mocks, or you have a large existing Jest test suite where migration risk is high.</p>
<p><strong>Choose @swc/jest</strong> when: you want a drop-in ts-jest replacement with 3-5x faster transforms, don&rsquo;t need type checking in tests (you run <code>tsc --noEmit</code> separately), and want zero config changes to existing Jest tests.</p>
<p><strong>Choose Vitest</strong> when: you&rsquo;re starting a new project, your production bundler is Vite, or you need the fastest possible test feedback loop and are willing to migrate away from Jest APIs.</p>
<h2 id="common-ts-jest-errors-and-how-to-fix-them">Common ts-jest Errors and How to Fix Them</h2>
<p>The most common ts-jest errors fall into three categories: module resolution failures, transform configuration mismatches, and type checking failures. Over 30% of repository setups with modern dependencies experience <code>transformIgnorePatterns</code> issues with ts-jest — typically caused by ESM-only npm packages that ts-jest doesn&rsquo;t transform by default. Understanding the root cause of each error category prevents hours of debugging. Module resolution failures (the &ldquo;Cannot find module&rdquo; error) almost always trace back to missing <code>moduleNameMapper</code> entries for TypeScript path aliases. Transform mismatches — particularly the <code>SyntaxError: Cannot use import statement outside a module</code> error — occur when an ESM-only npm package like <code>node-fetch</code>, <code>uuid</code>, or <code>chalk</code> is imported in a test but excluded from transformation by the default <code>transformIgnorePatterns</code>. Type checking failures, where tests pass in ts-jest but the production build fails <code>tsc</code>, typically indicate that <code>isolatedModules: true</code> is masking cross-file type errors that only full compilation catches. Each of the five errors below comes with a symptom, root cause diagnosis, and a concrete fix that works in Jest 29 and Jest 30.</p>
<h3 id="error-1-cannot-find-module-for-path-aliases">Error 1: &ldquo;Cannot find module&rdquo; for Path Aliases</h3>
<p><strong>Symptom:</strong> <code>Cannot find module '@/components/Button' from 'src/App.test.tsx'</code></p>
<p><strong>Cause:</strong> TypeScript path aliases (<code>@/</code>) are resolved by tsc during compilation but not by Jest&rsquo;s module resolver.</p>
<p><strong>Fix:</strong> Add <code>moduleNameMapper</code> to <code>jest.config.ts</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">moduleNameMapper</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#39;^@/(.*)$&#39;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&lt;rootDir&gt;/src/$1&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="error-2-syntaxerror-on-esm-package-import">Error 2: SyntaxError on ESM Package Import</h3>
<p><strong>Symptom:</strong> <code>SyntaxError: Cannot use import statement outside a module</code> when importing packages like <code>node-fetch</code>, <code>uuid</code>, or <code>chalk</code>.</p>
<p><strong>Cause:</strong> These packages ship ESM-only code. ts-jest&rsquo;s default <code>transformIgnorePatterns</code> excludes <code>node_modules</code>.</p>
<p><strong>Fix:</strong> Override <code>transformIgnorePatterns</code> to allow specific packages:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">transformIgnorePatterns</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#39;/node_modules/(?!(node-fetch|uuid|chalk)/)&#39;</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><h3 id="error-3-isolatedmodules-const-enum-error">Error 3: &ldquo;isolatedModules&rdquo; const enum Error</h3>
<p><strong>Symptom:</strong> <code>Error: 'const enums' are not supported. Use '--isolatedModules' flag is required.</code></p>
<p><strong>Cause:</strong> <code>const enum</code> requires cross-file compilation context that <code>isolatedModules: true</code> skips.</p>
<p><strong>Fix:</strong> Either remove <code>const enum</code> (replace with <code>enum</code> or <code>as const</code> objects), or disable <code>isolatedModules</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Option A: change const enum to regular enum
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">enum</span> <span style="color:#a6e22e">Status</span> { <span style="color:#a6e22e">Active</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>, <span style="color:#a6e22e">Inactive</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#39;</span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Option B: disable isolatedModules (slower)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> { <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, { <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">false</span> }] }
</span></span></code></pre></div><h3 id="error-4-types-dont-match-between-tests-and-source">Error 4: Types Don&rsquo;t Match Between Tests and Source</h3>
<p><strong>Symptom:</strong> Test passes but production build fails type checking.</p>
<p><strong>Cause:</strong> ts-jest with <code>diagnostics: false</code> or <code>isolatedModules: true</code> may miss cross-file type errors.</p>
<p><strong>Fix:</strong> Run <code>tsc --noEmit</code> as a separate CI step (see next section).</p>
<h3 id="error-5-jestconfigts-itself-fails-to-load">Error 5: jest.config.ts Itself Fails to Load</h3>
<p><strong>Symptom:</strong> <code>SyntaxError</code> or <code>Cannot find module</code> when Jest tries to read <code>jest.config.ts</code>.</p>
<p><strong>Cause:</strong> Jest 30 supports <code>jest.config.ts</code> natively, but older versions require ts-node or ts-jest/config imports.</p>
<p><strong>Fix:</strong> For Jest 30+, ensure you&rsquo;re using the latest Jest. For older versions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install --save-dev ts-node
</span></span></code></pre></div><p>Or rename to <code>jest.config.js</code> and use <code>require</code>.</p>
<h2 id="cicd-best-practices-type-checking--ts-jest-together">CI/CD Best Practices: Type Checking + ts-jest Together</h2>
<p>The modern TypeScript CI strategy separates type checking from test execution into two parallel jobs, combining the speed of <code>isolatedModules: true</code> with the safety of full type checking. Type checking runs <code>tsc --noEmit</code> against the full TypeScript project, while tests run through ts-jest with <code>isolatedModules</code> enabled. This two-stage approach gives you the best of both worlds: fast test feedback loops and guaranteed type correctness before merge.</p>
<p>Running <code>tsc --noEmit</code> catches errors that ts-jest with <code>isolatedModules: true</code> misses — particularly cross-file type errors, incorrect generic inference, and structural type mismatches. On a large codebase, running both in parallel rather than serially can save 5-8 minutes per CI run.</p>
<p><strong>GitHub Actions example (parallel jobs):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">name</span>: <span style="color:#ae81ff">CI</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>: [<span style="color:#ae81ff">push, pull_request]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">type-check</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/setup-node@v4</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">node-version</span>: <span style="color:#e6db74">&#39;20&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">cache</span>: <span style="color:#e6db74">&#39;npm&#39;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">npm ci</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">npx tsc --noEmit</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">test</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/setup-node@v4</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">node-version</span>: <span style="color:#e6db74">&#39;20&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">cache</span>: <span style="color:#e6db74">&#39;npm&#39;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">npm ci</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">npx jest --coverage --ci</span>
</span></span></code></pre></div><h3 id="caching-dependencies-in-ci">Caching Dependencies in CI</h3>
<p>Cache the <code>node_modules</code> directory to avoid reinstalling ts-jest and TypeScript on every run:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/cache@v4</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">path</span>: <span style="color:#ae81ff">~/.npm</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">key</span>: <span style="color:#ae81ff">${{ runner.os }}-node-${{ hashFiles(&#39;**/package-lock.json&#39;) }}</span>
</span></span></code></pre></div><h3 id="ts-jest-diagnostics-configuration-for-ci">ts-jest Diagnostics Configuration for CI</h3>
<p>In CI, you may want to fail on type errors that local development ignores. Set <code>diagnostics: true</code> with <code>warnOnly: false</code> for strict CI:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">transform</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#39;^.+\\.tsx?$&#39;</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ts-jest&#39;</span>, {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">isolatedModules</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">diagnostics</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">warnOnly</span>: <span style="color:#66d9ef">false</span>,       <span style="color:#75715e">// fail the build on type errors
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#a6e22e">exclude</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;/node_modules/&#39;</span>] <span style="color:#75715e">// don&#39;t type-check dependencies
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>  }]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For local development, flip <code>warnOnly: true</code> so type errors show as warnings without blocking your test run.</p>
<hr>
<h2 id="faq">FAQ</h2>
<p>These are the most frequently asked questions about ts-jest configuration, migration, and troubleshooting in 2026. The answers cover the most common pain points developers encounter when integrating TypeScript with Jest: transformer selection, performance optimization, ESM compatibility, error resolution, and framework choice. Each answer is actionable — covering not just what to do, but why the decision matters for your specific project context. Whether you&rsquo;re setting up ts-jest for the first time, migrating from an older version, or deciding between ts-jest and Vitest, these answers reflect the current state of the TypeScript testing ecosystem as of ts-jest v29.4.9 and Jest 30. The ts-jest package receives 22.7 million weekly downloads and is the default TypeScript transformer for Jest-based projects worldwide, making these questions relevant to the majority of TypeScript development teams in 2026.</p>
<h3 id="what-is-the-difference-between-ts-jest-and-babel-for-typescript-testing">What is the difference between ts-jest and Babel for TypeScript testing?</h3>
<p>ts-jest uses the actual TypeScript compiler (<code>tsc</code>) to transform test files, which means it can optionally perform real type checking during the test run. Babel with <code>@babel/preset-typescript</code> strips types entirely without any validation — it transpiles faster but catches zero type errors. The practical consequence: with ts-jest and <code>diagnostics: true</code>, a type mismatch in a test file fails the test run before any test executes. With Babel, the same error is silent. In 2026, the recommended pattern for projects that need both speed and safety is ts-jest with <code>isolatedModules: true</code> (fast transforms, no cross-file type analysis) plus <code>tsc --noEmit</code> as a parallel CI job (full type checking, no test execution).</p>
<h3 id="how-do-you-migrate-from-ts-jest-294x-to-jest-30">How do you migrate from ts-jest 29.4.x to Jest 30?</h3>
<p>Upgrade <code>jest</code> and <code>@types/jest</code> to <code>30.x</code>, then update <code>ts-jest</code> to the latest v29.4.x (which supports both Jest 29 and 30). The main breaking change is the configuration format: the legacy <code>globals['ts-jest']</code> style is removed in Jest 30. Replace it with the array transform format: <code>transform: { '^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.json' }] }</code>. You can also convert <code>jest.config.js</code> to <code>jest.config.ts</code> — Jest 30 supports TypeScript config files natively without ts-node. Run <code>npx jest --clearCache</code> after upgrading to clear stale transform caches that can cause false errors on first run.</p>
<h3 id="does-isolatedmodules-true-disable-typescript-type-checking-completely">Does isolatedModules: true disable TypeScript type checking completely?</h3>
<p>No — it disables cross-file type analysis, not intra-file type checking. With <code>isolatedModules: true</code>, ts-jest transforms each file independently without loading the full TypeScript project graph. Type errors within a single file (wrong argument type, missing property on a local interface defined in the same file) are still caught. What is disabled is analysis that requires importing type information from other modules — <code>const enum</code> declarations, namespace imports, and type constraints that reference types from separate files. To get full cross-file type safety alongside <code>isolatedModules: true</code>, run <code>tsc --noEmit</code> as a separate CI step.</p>
<h3 id="how-do-you-fix-syntaxerror-when-ts-jest-imports-esm-packages">How do you fix SyntaxError when ts-jest imports ESM packages?</h3>
<p>Override <code>transformIgnorePatterns</code> to include the ESM-only package in ts-jest&rsquo;s transform pipeline. The default pattern excludes all of <code>node_modules</code>, but ESM-only packages like <code>node-fetch</code>, <code>uuid</code>, <code>chalk</code>, and <code>nanoid</code> must be transformed. The fix: <code>transformIgnorePatterns: ['/node_modules/(?!(node-fetch|uuid|chalk)/)']</code>. For multiple packages, extend the alternation group using pipe separators. A more complete solution for projects with many ESM dependencies is enabling ts-jest&rsquo;s ESM mode (<code>useESM: true</code> in the transform options) combined with <code>--experimental-vm-modules</code> in the Node.js test runner — this avoids maintaining a growing exclusion list.</p>
<h3 id="should-you-use-ts-jest-or-vitest-for-a-new-typescript-project-in-2026">Should you use ts-jest or Vitest for a new TypeScript project in 2026?</h3>
<p>For most new TypeScript projects not targeting React Native, Vitest is the better default — it is 5–28x faster than ts-jest, has first-class ESM support built in, and requires minimal configuration for TypeScript projects. ts-jest remains the correct choice for React Native projects (which require Jest&rsquo;s jsdom environment and React Native-specific jest presets), large existing Jest codebases where migration cost outweighs speed gains, and projects that depend heavily on Jest-specific APIs (<code>jest.mock()</code>, <code>jest.spyOn()</code>, <code>jest.useFakeTimers()</code>) where Vitest&rsquo;s behavioral differences introduce migration risk. If you need Jest API compatibility with faster transforms, <code>@swc/jest</code> is a drop-in ts-jest replacement using SWC (a Rust-based compiler) at 10–30x the speed without type checking.</p>
]]></content:encoded></item></channel></rss>