Today I’m happy to share that Ionic’s starter projects now ship with modern linting, powered by ESLint. This gives us a consistent linting tool across all project types, but also removes a deprecated dependency. Let’s dive in and rewind the clock to 2018.

Sunsetting TSLint

If you were looking for a linting tool for a TypeScript project, chances are you ended up using TSLint. TSLint was a static analysis tool that would check a TypeScript code base to enforce a consistent code style and make sure the project didn’t break any rules, aka your standard linting tool. Now, TSLint isn’t the only tool in this space. Another linting tool that was the de facto standard in JavaScript projects was ESLint. ESLint and TSLint provided a similar set of features, but ESLint didn’t have support for TypeScript.

Fast forward to 2019, and the landscape was starting to look different. The TypeScript community was looking to improve the tooling landscape and provide a more unified experience for TypeScript and JavaScript devs. The maintainers of TSLint took this opportunity to announce the deprecation of TSLint and plans to help support ESLint as the linter for both TypeScript and JavaScript.

Now in 2021, the TSLint repo has been archived and the TypeScript-ESLint team, headed up by James Henry and others, have been working to support developers.

From one linting tool to another

With the history listed out of the way, let’s upgrade a project to use ESLint. This process can be done manually, but why do that when we can automate it. For most projects, the quickest way to upgrade is to use Angular-ESLint and run their schematics. First commit any open changes and make a new branch (just to be sure):

git add .
git commit -m "pre eslint"
git checkout -b feat-eslint

Then we can add the schematics for Angular-ESLint

ng add @angular-eslint/schematics

From here, we can convert our existing TSLint config to ESLint with the following command:

ng g @angular-eslint/schematics:convert-tslint-to-eslint {{YOUR_PROJECT_NAME_GOES_HERE}}

Once this is done, you can run ESLint against your project! The schematic won’t remove TSLint from your package.json or the tslint.json from your project. These can be removed:

rm tslint.json
npm uninstall tslint

Commit your changes and make send one sweet pull request!

Where do we go now?

With ESLint installed and configured, projects can make use of the extensive ecosystem of plugins available, like Prettier. With new Ionic Angular projects using ESLint, you can rest assured knowing you will have stable tools for the future of your development. A huge thank you to James Henry and Brad Zacher for building and maintaining TypeScript-ESLint and Angular-ESLint.

Signup for the Ionic Newsletter to get the latest news and updates!

Notable Replies

  1. According to this configuration, WebStorm prompts me
    ESLint: Specify the path of the’eslint’ package
    I am a loyal fan of ionic and I have been using it, how can I solve this, I didn’t use ESLint before,

    “name”: “utsource”,
    “version”: “5.4.1”,
    “author”: “张玉海”,
    “homepage”: “”,
    “scripts”: {
    “ng”: “ng”,
    “start”: “ionic serve --hmr”,
    “build”: “ng build”,
    “test”: “ng test”,
    “lint”: “ng lint”,
    “e2e”: “ng e2e”
    “private”: true,
    “dependencies”: {
    @angular/common”: “^11.2.8”,
    @angular/core”: “^11.2.8”,
    @angular/forms”: “^11.2.8”,
    @angular/platform-browser”: “^11.2.8”,
    @angular/platform-browser-dynamic”: “^11.2.8”,
    @angular/router”: “^11.2.8”,
    @ionic-native/app-version”: “^5.32.0”,
    @ionic-native/camera”: “^5.32.0”,
    @ionic-native/code-push”: “^5.32.0”,
    @ionic-native/core”: “^5.32.0”,
    @ionic-native/facebook”: “^5.32.0”,
    @ionic-native/file”: “^5.32.0”,
    @ionic-native/file-chooser”: “^5.32.0”,
    @ionic-native/file-transfer”: “^5.32.0”,
    @ionic-native/geolocation”: “^5.32.0”,
    @ionic-native/google-plus”: “^5.31.1”,
    @ionic-native/in-app-browser”: “^5.32.0”,
    @ionic-native/qr-scanner”: “^5.32.0”,
    @ionic-native/social-sharing”: “^5.31.1”,
    @ionic-native/splash-screen”: “^5.31.1”,
    @ionic-native/status-bar”: “^5.31.1”,
    @ionic-native/toast”: “^5.31.1”,
    @ionic/angular”: “^5.6.1”,
    @ionic/storage”: “^2.3.0”,
    @ngx-translate/core”: “^13.0.0”,
    @ngx-translate/http-loader”: “^6.0.0”,
    “cordova-android”: “9.0.0”,
    “cordova-ios”: “6.1.1”,
    “cordova-plugin-app-version”: “0.1.9”,
    “cordova-plugin-code-push”: “^2.0.0”,
    “cordova-plugin-compat”: “^1.2.0”,
    “cordova-plugin-device”: “^2.0.3”,
    “cordova-plugin-facebook4”: “^6.4.0”,
    “cordova-plugin-file”: “^6.0.2”,
    “cordova-plugin-file-transfer”: “^1.7.1”,
    “cordova-plugin-filechooser”: “1.2.0”,
    “cordova-plugin-googleplus”: “^8.5.2”,
    “cordova-plugin-ionic-keyboard”: “^2.2.0”,
    “cordova-plugin-ionic-webview”: “^5.0.0”,
    “cordova-plugin-splashscreen”: “^6.0.0”,
    “cordova-plugin-statusbar”: “^2.4.3”,
    “cordova-plugin-x-socialsharing”: “^6.0.3”,
    “cordova-plugin-x-toast”: “2.7.2”,
    “cordova-plugin-zip”: “^3.1.0”,
    “core-js”: “^3.10.0”,
    “echarts”: “^5.0.2”,
    “rxjs”: “^6.6.7”,
    “tslib”: “^2.2.0”,
    “zone.js”: “^0.11.4”
    “devDependencies”: {
    @angular-devkit/architect”: “^0.1102.7”,
    @angular-devkit/build-angular”: “^0.1102.7”,
    @angular-devkit/core”: “^11.2.7”,
    @angular-devkit/schematics”: “^11.2.7”,
    @angular-eslint/builder”: “2.1.0”,
    @angular-eslint/eslint-plugin”: “2.1.0”,
    @angular-eslint/eslint-plugin-template”: “2.1.0”,
    @angular-eslint/schematics”: “2.1.0”,
    @angular-eslint/template-parser”: “2.1.0”,
    @angular/cli”: “11.2.7”,
    @angular/compiler”: “^11.2.8”,
    @angular/compiler-cli”: “^11.2.8”,
    @angular/language-service”: “^11.2.8”,
    @ionic/angular-toolkit”: “^3.1.1”,
    @types/jasmine”: “^3.6.7”,
    @types/jasminewd2”: “~2.0.6”,
    @types/node”: “^14.14.37”,
    @typescript-eslint/eslint-plugin”: “4.16.1”,
    @typescript-eslint/parser”: “4.16.1”,
    “code-push”: “^3.1.5”,
    “codelyzer”: “^6.0.1”,
    “cordova-plugin-advanced-http”: “^3.1.0”,
    “cordova-plugin-camera”: “^5.0.1”,
    “cordova-plugin-dialogs”: “^2.0.2”,
    “cordova-plugin-inappbrowser”: “^5.0.0”,
    “cordova-plugin-whitelist”: “^1.3.4”,
    “eslint”: “^7.6.0”,
    “eslint-plugin-import”: “2.22.1”,
    “eslint-plugin-jsdoc”: “30.7.6”,
    “eslint-plugin-prefer-arrow”: “1.2.2”,
    “jasmine-core”: “~3.6.0”,
    “jasmine-spec-reporter”: “~6.0.0”,
    “karma”: “~5.2.3”,
    “karma-chrome-launcher”: “~3.1.0”,
    “karma-coverage-istanbul-reporter”: “~3.0.2”,
    “karma-jasmine”: “^4.0.1”,
    “karma-jasmine-html-reporter”: “^1.5.4”,
    “protractor”: “~7.0.0”,
    “ts-node”: “~9.0.0”,
    “typescript”: “~4.1.5”
    “description”: “An Ionic project”,
    “cordova”: {
    “plugins”: {
    “cordova-plugin-device”: {},
    “cordova-plugin-ionic-keyboard”: {},
    “cordova-plugin-filechooser”: {},
    “cordova-plugin-x-toast”: {},
    “cordova-plugin-app-version”: {},
    “cordova-plugin-zip”: {},
    “cordova-plugin-file”: {},
    “cordova-plugin-facebook4”: {
    “APP_ID”: “1543677069111040”,
    “APP_NAME”: “Utsource”,
    “cordova-plugin-x-socialsharing”: {},
    “cordova-plugin-code-push”: {},
    “cordova-plugin-ionic-webview”: {},
    “cordova-plugin-statusbar”: {},
    “cordova-plugin-file-transfer”: {},
    “cordova-plugin-splashscreen”: {},
    “cordova-plugin-whitelist”: {},
    “cordova-plugin-camera”: {
    “cordova-plugin-inappbrowser”: {}
    “platforms”: [

  2. Ok!
    What ide do you use now?
    Are you using Visual Studio Code?

  3. Vim! But Vscode should still behave the same

  4. A more “correct” setup should be

      "extends": "./tsconfig.json",
      "compilerOptions": {
        "outDir": "./out-tsc/app",
        "types": []
      "files": ["src/main.ts", "src/polyfills.ts"],
      "include": ["src/**/*.d.ts"]


      "compileOnSave": false,
      "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "downlevelIteration": true,
        "experimentalDecorators": true,
        "moduleResolution": "node",
        "importHelpers": true,
        "target": "es2015",
        "module": "es2020",
        "lib": ["es2018", "dom"]
      "angularCompilerOptions": {
        "enableI18nLegacyMessageIdFormat": false,
        "strictInjectionParameters": true,
        "strictInputAccessModifiers": true,
        "strictTemplates": true

Join the discussion on the Ionic Forum

2 more replies