Skip to content

chore(many): upgrade Typescript - #31280

Open
thetaPC wants to merge 25 commits into
major-9.0from
FW-6257
Open

chore(many): upgrade Typescript#31280
thetaPC wants to merge 25 commits into
major-9.0from
FW-6257

Conversation

@thetaPC

@thetaPC thetaPC commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Issue number: resolves #29716


What is the current behavior?

The Ionic Framework packages each carried their own independent TypeScript configuration, and only @ionic/angular-server extended from the root tsconfig.json. As a result the settings had drifted apart across packages:

  • moduleResolution used the classic node resolver in several packages.
  • lib was pinned to es2017 (and other mixed values).
  • module was set to older values such as es2015 in places.
  • target ranged from es2015 to es2020 depending on the package.

This made the setup harder to maintain and kept the framework on older TypeScript defaults.

Linting had drifted the same way. Most packages ran ESLint 7 with @typescript-eslint 5/6 and legacy .eslintrc config, while @ionic/angular had already moved to ESLint 9 with flat config. The older @typescript-eslint parsers predate TypeScript 6 and do not officially support it, so once the framework moves to TypeScript 6 the linter can no longer reliably parse the source it is checking.

What is the new behavior?

Modernizes the TypeScript setup so every package shares a single base configuration and modern compiler options.

  • A shared base config now lives at core/tsconfig.base.json, and every package extends it, keeping only package specific overrides locally. It lives in core/ rather than at the repo root because core is the only package present in every build context, including the Vercel preview which builds core in isolation. Since every package already depends on core, the base is reachable everywhere without any per environment workarounds.
  • Removed the stale @ionic/* path aliases from the old root config. Three of them pointed at directories that no longer exist since the packages moved under packages/.
  • moduleResolution updated from node to bundler.
  • lib updated to es2022.
  • module updated to esnext.
  • target updated to es2022.
  • TypeScript upgraded to 6.x across the packages to align with @ionic/angular.

Changes that followed from the above:

@ionic/react

  • Added declare to the inherited context class fields, required by the es2022 useDefineForClassFields default.
  • Added a tsconfig.spec.json with isolatedModules so ts-jest runs under TypeScript 6.
  • Added the generated components.ts to .prettierignore so core builds no longer reformat it. It is regenerated by core's Stencil React output target, so like the other generated files it should not be prettier formatted.
  • Removed the dead proxies.ts entry, which has matched no file since chore(deps): update react and stencil/react-output-target #31063 replaced the old proxies.ts with components.ts.
  • Upgraded @testing-library/react from 13 to 16 (and added its now required @testing-library/dom peer), which clears the ReactDOMTestUtils.act deprecation warning in the test run.
  • Upgraded @testing-library/jest-dom from 5 to 7, updated a spec's import from the removed @testing-library/jest-dom/extend-expect path to @testing-library/jest-dom.

@ionic/react-router

  • Added declare to the inherited context class field, required by the es2022 useDefineForClassFields default (the same change made in @ionic/react).
  • Removed an unused import surfaced by full type checking.
  • Added a non-null assertion for a value the type checker could no longer narrow.
  • Added rootDir so @rollup/plugin-typescript can determine the output file layout, which resolves the TS5011 advisory it otherwise emits.
  • Added a Check Diff step to the CI build for parity with @ionic/react, @ionic/vue, and @ionic/angular. After the build it asserts a clean git tree, so unformatted committed source now fails CI. A preceding step reverts the core/package.json and packages/react/package.json updates that npm run sync and the react build archive produce, since those are expected.
  • Formatted pre-existing source with prettier so the new Check Diff passes. Formatting only, no behavior change.

@ionic/vue

  • Config only. Extends the base config and moves to TypeScript 6 with no source changes required.
  • rootDir was intentionally not added. It surfaces a set of pre-existing type errors, which are deferred to a dedicated PR so this one stays focused on the setup.

@ionic/vue-router

  • Config only. Extends the base config and moves to TypeScript 6 with no source changes required.
  • rootDir deferred for the same reason as @ionic/vue.
  • Added the same Check Diff step for parity. Its preceding cleanup reverts only core/package.json, since the vue build archive does not include a package.json.
  • Formatted pre-existing source with prettier so the new Check Diff passes. Formatting only, no behavior change.

@ionic/angular

  • Dropped the redundant tsc script, since the build already type checks like the other packages.
  • Deleted the leftover pre-ng-packagr build files (scripts/rollup.config.js, scripts/rollup.config.legacy.js, scripts/package.json). These date back to the manual tsc plus rollup plus gulp pipeline Ionic used before migrating to ng-packagr around Angular 9, for Angular versions no longer supported (this branch supports Angular 18 and above).
  • Removed the now unused outDir, which existed to hold the tsc output that the pre-ng-packagr pipeline bundled from. ng-packagr manages its own output, so it no longer serves a purpose.

ESLint 9 and flat config

Moving to TypeScript 6 leaves the old linter behind, so the ESLint setup is upgraded to match. @ionic/angular had already migrated to ESLint 9 with flat config; this brings the remaining packages in line with it.

  • Upgraded eslint from 7 to 9 and @typescript-eslint/* from 5/6 to 8 across core, @ionic/react, @ionic/react-router, @ionic/vue, @ionic/vue-router, and @ionic/angular-server. @ionic/angular was already on these versions.
  • Migrated each package's .eslintrc.* and .eslintignore to eslint.config.js (flat config). The shared @ionic/eslint-config is still authored in eslintrc format, so it is bridged through FlatCompat, the same approach @ionic/angular already uses.
  • Added an npm overrides entry, scoped to @ionic/eslint-config, in every package. @ionic/eslint-config@0.3.0 still declares an ancient @typescript-eslint@^4 (and an old eslint-plugin-import), which npm installs nested inside it. Those versions peer require an ESLint older than 9, so under ESLint 9 they break npm install with an ERESOLVE and the import/newline-after-import rule crashes on the removed context.getScope. The override forces @ionic/eslint-config's copies up to the versions the package already declares, so the stale incompatible packages never get installed and npm install and npm ci resolve cleanly.
  • Declared eslint-plugin-import to ^2.31.0 in each package. The version @ionic/eslint-config pulls in transitively (^2.22.0) resolves an ESLint 9 incompatible release whose import/newline-after-import rule calls the removed context.getScope and crashes the run. @ionic/angular already declares this plugin directly for the same reason.
  • Added an explicit typescript devDependency to core. It did not declare one, and the ESLint 9 type aware parser needs a TypeScript that understands the bundler moduleResolution used in the shared tsconfig.
  • Kept rule behavior faithful to the previous configs. ban-types was removed in @typescript-eslint 8, so its successors no-empty-object-type and no-unsafe-function-type are turned off to preserve the old stance. no-unused-expressions is configured with allowShortCircuit and allowTernary to keep the existing short-circuit side-effect style. core turns off prefer-optional-chain to match the other packages, because the stricter rule in @typescript-eslint 8 flagged datetime guards where optional chaining would break type narrowing.
  • Removed stale eslint-disable directives that no longer suppress anything, and applied the few small fixes the stricter ruleset surfaced, such as wrapping two conditionals in Boolean() and dropping unused catch bindings.
  • Regenerated the lockfiles so only the ESLint dependency tree changed.

Does this introduce a breaking change?

  • Yes
  • No

The compiled output now targets es2022 (previously es2015/es2017), and building the framework requires TypeScript 6.x. Consuming applications should be on a toolchain that supports es2022 output.

Other information

The FlatCompat bridge, the @ionic/eslint-config overrides, the direct eslint-plugin-import requirement, and core's explicit typescript devDependency are only needed because the shared @ionic/eslint-config is behind the current ESLint ecosystem. To let the framework drop them, @ionic/eslint-config should be updated to:

  • Ship a flat config native entry point (for example a flat/recommended export) so consumers no longer need FlatCompat or the @eslint/eslintrc and @eslint/js bridge dependencies.
  • Raise its dependency floors to the ESLint 9 ecosystem: @typescript-eslint/* 8, eslint-plugin-import 2.31 or newer, eslint-config-prettier 10, and an eslint peer of 9.

Once that ships, each package here can drop FlatCompat, the bridge dependencies, the @ionic/eslint-config overrides, the direct eslint-plugin-import requirement, and core's explicit typescript, leaving a much smaller eslint.config.js.

@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview Jul 29, 2026 11:26pm

Request Review

@github-actions github-actions Bot added package: core @ionic/core package package: angular @ionic/angular package package: vue @ionic/vue package package: react @ionic/react package labels Jul 21, 2026
Comment thread packages/react-router/tslint.json Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't being used anywhere, RR uses ESLint.

"fs-extra": "^9.0.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"np": "^6.4.0",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being used

Comment thread packages/vue/package.json
},
"homepage": "https://github.com/ionic-team/ionic-framework#readme",
"devDependencies": {
"@babel/types": "^7.18.4",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being used

@thetaPC
thetaPC marked this pull request as ready for review July 30, 2026 02:10
@thetaPC
thetaPC requested a review from a team as a code owner July 30, 2026 02:10
@thetaPC
thetaPC requested a review from BenOsodrac July 30, 2026 02:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: angular @ionic/angular package package: core @ionic/core package package: react @ionic/react package package: vue @ionic/vue package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant