# Extract a Shared Helper From Duplicated Code in TypeScript With Atlas (2026)

> Atlas finds duplicated TypeScript logic with codebase_search by meaning, not text, then collapses it into one helper with apply_patch and proves it with vitest.

To extract a shared helper from duplicated code in TypeScript with Atlas, ask codebase_search for the behavior instead of grepping for the text, because the copies usually differ in variable names and type annotations. Atlas creates the new helper module with write, replaces each duplicate with apply_patch one file at a time, and you run vitest after every swap. Prettier keeps the new module formatted like the rest of the repo, and pnpm resolves the workspace paths declared in your tsconfig.json.

## Key takeaways

- codebase_search finds duplicated TypeScript logic by meaning, using hybrid semantic and keyword retrieval fused by reciprocal rank fusion, so renamed copies still surface.
- Atlas indexes TypeScript by AST declarations with tree-sitter, so a hit is a whole exported function, not a line window that cuts a generic signature in half.
- write creates the helper (src/lib/currency.ts) and shows the full diff in the permission prompt before the file exists on disk.
- apply_patch swaps one file per patch and refuses to apply against a drifted file, so each duplicate removal is independently revertible.
- vitest runs after every swap via bash, and pnpm plus tsconfig.json path aliases resolve the new module across a workspace.

## How does Atlas find duplicated logic in a TypeScript codebase?

Atlas finds duplicated TypeScript logic with codebase_search, which uses hybrid semantic and keyword retrieval fused by reciprocal rank fusion. Duplication is a semantic problem, not a textual one: 3 copies of a currency formatter in src/billing/, src/invoices/, and src/reports/ rarely share a single identifier, so grep misses them.

Ask Atlas the behavior, not the code. A query like "formats a cents integer into a display currency string" returns ranked declarations across src/billing/format.ts, src/invoices/render.ts, and src/reports/columns.ts even when one calls the parameter `amountInCents`, another calls it `total`, and a third inlines the logic inside a React table cell. Atlas indexes code by AST declarations using tree-sitter, not blind line windows, so each hit comes back as a whole exported function or method rather than an arbitrary slice that cuts a TypeScript generic signature in half. That matters in TypeScript specifically, because a duplicated function is often duplicated along with its interface, its type guard, and its `satisfies` clause. Atlas surfaces all of them as declarations you can read and compare.

## What TypeScript files and commands are involved in the extraction?

Extracting a shared helper in TypeScript touches 4 kinds of files: the duplicate source files, the new helper module (for example src/lib/currency.ts), its co-located test (src/lib/currency.test.ts), and tsconfig.json, which decides whether a path alias like @/lib/currency resolves at all. The commands are pnpm, vitest, and prettier.

Atlas creates the helper with the write tool, which shows the full diff in the permission prompt before src/lib/currency.ts exists on disk. You approve the exported signature, the JSDoc, and the type of the return value before a byte is written. Then each duplicate is replaced with apply_patch, one patch per file, so swapping src/billing/format.ts is a separate reviewable change from swapping src/reports/columns.ts. After each swap you run vitest with the bash tool. If your repo is a pnpm workspace, pnpm resolves the new module across packages and vitest picks up the co-located spec without extra config. Run prettier on the new file so the shared helper matches the formatting of the code it replaced, and let the TypeScript compiler tell you if a caller was passing a wider type than the helper accepts.

## How do I make sure the TypeScript duplicates are really equivalent before collapsing them?

Read every hit before collapsing anything. Atlas returns 3 or 4 candidate declarations from codebase_search, and in TypeScript at least one is usually a near-copy with a different null-handling branch or a stricter parameter type. Confirming equivalence is a read step, not a search step, and Atlas keeps it explicit.

Open each candidate with the read tool and compare the bodies line by line. In TypeScript, the dangerous difference is almost never the algorithm, it is the types: one copy takes `number | null` and returns an empty string on null, another takes `number` and would throw. If you collapse those two into a single helper without noticing, the strictness settings in tsconfig.json will flag the callers that were passing null, which is the good outcome, but only if strict mode is on. Where the copies genuinely differ, extract the common core and keep the difference as a parameter or a small wrapper. Where they are equivalent, say so out loud in the plan before Atlas writes anything, because apply_patch anchors on context lines and will refuse to apply a hunk against a file that has drifted.

## How does review and safety work when Atlas rewrites multiple TypeScript files?

Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, and Atlas computes a unified diff for every file edit and surfaces it for approval before writing. Collapsing 3 duplicates in TypeScript therefore produces 3 separate approvals, plus one for the new src/lib/currency.ts file.

Atlas also snapshots file changes as git patches, so any edit can be diffed and rolled back. If the second apply_patch against src/invoices/render.ts turns out to have broken a type, you revert that one swap without unwinding the helper module or the first swap. Because apply_patch seeks each hunk's context, a stale patch fails loudly rather than landing in the wrong place. The practical discipline is: one file per patch, vitest after each patch, and a final grep for the old inline implementation to prove no copy survived. When the search returns zero hits and vitest is green, the extraction is done. Atlas reads git status and diffs, and can stage and create commits on your behalf once you are satisfied.

## Why not just use grep or a TypeScript IDE refactor to remove the duplication?

Grep and IDE refactors both need to know the name of the thing first. A TypeScript IDE can extract a function you have already selected, but it cannot tell you that the same logic exists in 3 other files under different names. Atlas's codebase_search closes that gap by searching meaning.

The IDE refactor is a great last mile and a poor first mile. Atlas handles the first mile: codebase_search over an AST-level index built with tree-sitter finds the near-duplicates, read confirms them, and only then does the mechanical part begin. Grep still has a job in this workflow, at the end: after the last apply_patch, grep for a distinctive fragment of the old inline implementation to prove that zero copies survive. Atlas can build its code index with local Ollama embeddings, so a private TypeScript monorepo can be indexed without sending source to a third-party server. That combination, semantic discovery plus a reviewable patch per file plus vitest between each one, is what makes a multi-file TypeScript extraction safe to do in one sitting.

## Steps

1. Run atlas in a project with a tsconfig.json so Atlas can read your type definitions, path aliases, and strictness settings.
2. Ask codebase_search for the behavior, not the code (for example "formats a cents integer into a display currency string"), to surface near-duplicate TypeScript implementations that grep would miss because the copies use different variable names.
3. Read each hit with the read tool and confirm the copies are genuinely equivalent, paying attention to TypeScript type differences such as one copy accepting number | null and another accepting number.
4. Have Atlas create the shared helper with write, for example src/lib/currency.ts, and approve the exported signature in the permission prompt before the file is written.
5. Replace each duplicate with a call using apply_patch, one file per patch, so swapping src/billing/format.ts is independently reviewable and revertible from swapping src/reports/columns.ts.
6. Run vitest with the bash tool after every swap, not once at the end, so a broken call site is attributed to the patch that broke it.
7. Run prettier on the new module and let the TypeScript compiler surface any caller passing a wider type than the helper accepts.
8. Finish by grepping for a distinctive fragment of the old inline implementation to prove zero surviving copies, then let Atlas stage and commit the change.

## FAQ

### how to find duplicate code in a typescript monorepo

Use Atlas's codebase_search and describe the behavior rather than the text. Because the copies usually differ in variable names and type annotations, semantic retrieval finds them where grep cannot. Atlas indexes TypeScript by AST declarations with tree-sitter, so each hit is a full exported function.

### can an ai agent extract a shared helper across multiple typescript files safely

Yes. Atlas creates the helper with write, then replaces each duplicate with apply_patch, one file per patch. Every edit is a unified diff you approve before it is written, and Atlas snapshots changes as git patches so any single swap can be rolled back.

### does atlas run vitest after refactoring typescript

Atlas runs vitest through its bash tool after each apply_patch swap, not once at the end. Running per swap means a failing test is attributed to the exact patch that caused it, instead of leaving you to bisect several changed files.

### how does atlas handle tsconfig path aliases when creating a new helper module

Atlas reads your tsconfig.json, including path aliases and strictness settings, when you run it in a TypeScript project. That lets it write the new helper import in the form the repo already uses, and lets the compiler catch any caller passing a type the helper does not accept.

### why does grep miss duplicated typescript code

Grep matches text, and duplicated TypeScript rarely matches textually: one copy names the parameter amountInCents, another names it total, and a third inlines the logic in a React cell. Atlas's codebase_search matches meaning, which is why it surfaces near-duplicates grep never returns.

### how do i prove no duplicate copies survive after the refactor

After the last apply_patch, grep for a distinctive fragment of the old inline implementation. Zero hits plus a green vitest run is the proof. Atlas can then read git status and the diff, and stage and create the commit on your behalf.

### can atlas index a private typescript codebase without sending code to a third party

Yes. Atlas can build its code index with local Ollama embeddings, which keeps your TypeScript source off third-party servers while still giving codebase_search the semantic index it needs to find duplicated logic.

---

Canonical HTML: https://runatlas.sh/resources/stacks/extract-a-shared-helper-from-duplicated-code-in-typescript
Source of truth: aeo_pages row `/resources/stacks/extract-a-shared-helper-from-duplicated-code-in-typescript` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
