Skip to content
Fran Gonzalez
← Back to blog
(updated Sep 30, 2026)·Clanker·4 min read

Migrating a Tauri app to TypeScript 7 and Oxlint

How I replaced ESLint and typescript-eslint with Oxlint to unlock the native TypeScript 7 compiler in a Vite + Tauri repo.

Some matmuls wrote this slop, sorry. My goal with this content is to document some work I (a real human bean) do while poking the Clanker, and try to learn something along the way.

I migrated a Tauri + React repo from TypeScript 6.0.3 to TypeScript 7.0.2 (the native Go compiler) and replaced the entire ESLint stack with Oxlint. The migration was one PR; the blocker was the ESLint toolchain rather than the compiler itself.

Note

Tested with TypeScript 7.0.2, Oxlint 1.82.0, and Tauri 2.11.4 on 2026-09-12.

The Problem

TypeScript 7 is the native Go port of the compiler, shipping as typescript@7.x on npm with per-platform binaries. I wanted it in this repo for its speed, but typescript-eslint pins its peer dependency to typescript >=4.8.4 <6.1.0, and that range is semantic, not conservative.

I verified why. The typescript@7 npm package’s Node entry point is a lib/version.cjs stub that exports exactly two keys. The JS Compiler API that typescript-eslint’s type-aware rules are built on is gone from the package. No amount of peer-range wrangling fixes that; the rules have no engine to run on.

So the question became: what replaces recommendedTypeChecked? The answer is Oxlint’s type-aware mode, which runs tsgolint (a driver around the same native TS7 compiler) to power type-aware rules and even surface tsc diagnostics as lint errors.

What Changed

Before touching dependencies, I verified the native compiler handles the repo’s exact tsconfig surface. I built a scratch project mirroring it: tsc -b with project references, composite, tsBuildInfoFile incremental builds, noUncheckedIndexedAccess, exactOptionalPropertyTypes, moduleResolution: "Bundler", and allowImportingTsExtensions. All of it works on 7.0.2. Stale .tsbuildinfo files needed deleting once (the format changed); the typecheck and build:web scripts stayed byte-for-byte identical.

For the lint migration I used the official migration tool:

pnpm dlx @oxlint/migrate eslint.config.js --type-aware --details

Per the migration docs, this converts a flat ESLint config into .oxlintrc.json. The parity audit came out as:

  • 121 rules migrated, including all 23 type-aware rules from recommendedTypeChecked (no-floating-promises, await-thenable, and friends)
  • react-hooks and react-refresh rules map to Oxlint’s react/ plugin (react/exhaustive-deps, react/only-export-components)
  • 6 rules dropped, each with a stated reason: no-undef and no-useless-assignment are nursery-grade in Oxlint (and tsgo covers what no-undef caught), no-dupe-args and no-octal are superseded by ESM strict mode, and react-hooks/config/react-hooks/gating are React Compiler gating options this config never used

The dependency diff: out went eslint, @eslint/js, typescript-eslint, eslint-plugin-react-hooks, eslint-plugin-react-refresh, and globals; in came oxlint 1.82.0 and oxlint-tsgolint 7.0.2001. The lockfile shrank by 681 lines net.

Type-aware linting and tsc diagnostics are enabled from the config:

// .oxlintrc.json
{
  "options": {
    "typeAware": true,
    "typeCheck": true
  }
}

With typeCheck: true, pnpm lint reports compiler diagnostics inline: a deliberate export const canary: number = "not a number" produced typescript(TS2322) with a codespan from Oxlint itself, no separate tsc invocation.

The editor side (nvim, not VS Code): nvim-lspconfig’s tsc server detects TypeScript ≥7 in node_modules/.bin and runs tsc --lsp --stdio. The stable 7.0.2 binary accepts --lsp even though the flag isn’t in --help output. ts_ls, typescript-tools.nvim, and vtsls are all tsserver-based and don’t apply anymore. TS7 ships no tsserver, just the tsc binary and lib .d.ts files.

For Tauri specifically: nothing needed to change. tauri.conf.json shells out to pnpm build:web (tsc -b && vite build), frontendDist points at Vite output, and the crate has no tauri-specta TypeScript codegen. TS7 never emits here anyway; the repo has been noEmit + Vite since the scaffold.

Results

Measured on this repo (16 lintable files), before → after:

  • Lint: eslint . 1.8s → oxlint ~0.2s (type-aware, including tsc diagnostics)
  • Typecheck: tsc -b 0.77s → ~0.3s warm on the native compiler
  • Lockfile: −681 lines net
  • CI and Tauri build hooks: zero command changes

mise run verify passes every gate: format, Oxlint, tsgo typecheck, Vitest, node --test, the tsc -b && vite build production build, and the Rust side.

What I’d Do Differently

I spent a debugging cycle concluding that Oxlint silently ignored options.typeCheck from config while honoring it as a CLI flag. That was wrong. Re-running @oxlint/migrate --type-aware after my manual config edit had produced a duplicate "options" key in .oxlintrc.json. Last key wins in JSON parsing, so my typeCheck: true was clobbered by the regenerated { "typeAware": true } block. Prettier happily formats duplicate-key JSON without complaint. Next time I’ll validate the generated config for duplicate keys before blaming the tool.

References

This post was written with AI assistance.