Tslint: ordered-imports does not support path aliases defined in tsconfig

Created on 15 Jan 2018  ·  15Comments  ·  Source: palantir/tslint

Bug Report

  • __TSLint version__: 5.9.1
  • __TypeScript version__: 2.5.3
  • __Running TSLint via__: CLI (through Angular CLI)

TypeScript code being linted

import { CrudEntity } from '@app/shared/model/crud-entity.model';
import { EntityType } from '@app/structure/model/entity-type.enum';

with tslint.json configuration:

        "ordered-imports": [
            true,
            {
                "grouped-imports": true,
                "import-sources-order": "lowercase-last",
                "named-imports-order": "lowercase-first",
                "module-source-path": "full"
            }
        ],

Actual behavior

imports starting with @app are considered as imports from libraries.

Expected behavior

However, tsconfig file specifies @app as:

        "baseUrl": "./src",
        "paths": {
            "@app/*": [
                "app/*"
            ],
            "@env/*": [
                "environments/*"
            ]
        }

So those import shall be considered as parent imports.

Hard P3 Rule Enhancement 🌹 R.I.P. 🌹

Most helpful comment

Using #4134, you could do something like:

"rules": {
    "ordered-imports": [
        true,
        {
            "import-sources-order": "case-insensitive",
            "named-imports-order": "case-insensitive",
            "grouped-imports": true,
            "groups": [
                { "match": "^@app", "order": 20 },
                { "match": "^@env", "order": 30 },
                { "name": "parent_dir", "match": "^[.][.]", "order": 40 },
                { "name": "current dir", "match": "^[.]", "order": 50 },
                { "match": null, "order": 10 }
            ]
        }
    ]
}

Or whatever other way you may want to group the imports. We use something like this for our project with quite a few aliases for internal libraries and modules.

All 15 comments

Isn't that pretty confusing though? There are many libraries that _do_ use NPM scopes and therefore start with @. Not sure if it's worth adding the extra complexity to this rule to support usage like this.

It is a usage that is somehow popular in the Angular World, one can even find people promoting it (https://medium.com/@tomastrajan/6-best-practices-pro-tips-for-angular-cli-better-developer-experience-7b328bc9db81). But I do agree it may be confusing since NPM scopes start with @.

As a result I renamed my aliases as #app and #env. It compiles and runs fine, however, since those imports still do not start with .., they are still considered as library imports. I also tested app and envwhich run fine but also fail linting.

I agree not all developers are going to uses tsconfig aliases, but at least many Angular CLI developers are going to set baseUrl to ./src in their tsconfig.json file. As a result they can import files like 'app/my-module/my-module.module' which makes it very convenient not to use relative file paths for importing elements from other modules. In this case also, tslint will wrongly consider those imports as library imports.

I agree this is a huge use case. I do this for React as well to avoid long paths with many parent dir references (../). If tslint is aware of tsconfig.json, maybe it should parse paths and baseUrl and resolve them to ../ paths

Facing same issue here. Would really love to use grouped-imports, both in our ReactJS and NodeJS apps, but ordered-imports isn't able to tell library from non-library imports.

I would agree with this. Aliases are becoming more popular and this would be super useful.

I had the same issue and managed to fix it.

In tsconfig.json I have
"paths": { "@app/api": [ "./app/api/index" ] },

then in tslint.conf (still we check dependencies except optional):
"no-implicit-dependencies": [ true, "optional" ]

then finally in package.json (we have to have it in optional dependencies):
"optionalDependencies": { "@app/api": "app-hack" }

With such configuration I can use imports like below without any exception from TSLint:
import {DictionaryType} from '@app/api';

Hopefully that'll work for you :-)

What if we could tell TSLint which paths are in fact relative, even though they can look as absolute ones.

{
  "ordered-imports": [
    true,
    {
      "grouped-imports": true,
      ...
      "path-alias-roots": ["$@src/", "$@typings/"]
    }
  ]
}

I use the same trick (add alias to tsconfig.json) but on karma test I get

HeadlessChrome 67.0.3396 (Windows 10.0.0) ERROR
  Uncaught Error: Missing: SyncTestZoneSpec
  at http://localhost:9876/_karma_webpack_/vendor.bundle.js:270128

Did I miss something ?

What if we could tell TSLint which paths are in fact relative

Typed rules are able to access program.getCompilerOptions() so we shouldn't have to manually provide this in the rule's configuration.

Since the requested behavior is apparently popular in the Angular community, it seems reasonable that this could be made into an OptionallyTypedRule to enable an opt-in configuration setting to read path aliases from the compiler settings.

@ppowstanski Late on the conversation, but we can simply define it like this:

"no-implicit-dependencies": [true, ["dev", "@src"]], // add any alias in the array

And avoid having to add fake optional dependencies in the package.json.

I'd prefer looking at program.getCompilerOptions() (as @JoshuaKGoldberg suggested) rather than adding additional rule configuration. That way, there's only one source of truth for custom path mappings.

Using #4134, you could do something like:

"rules": {
    "ordered-imports": [
        true,
        {
            "import-sources-order": "case-insensitive",
            "named-imports-order": "case-insensitive",
            "grouped-imports": true,
            "groups": [
                { "match": "^@app", "order": 20 },
                { "match": "^@env", "order": 30 },
                { "name": "parent_dir", "match": "^[.][.]", "order": 40 },
                { "name": "current dir", "match": "^[.]", "order": 50 },
                { "match": null, "order": 10 }
            ]
        }
    ]
}

Or whatever other way you may want to group the imports. We use something like this for our project with quite a few aliases for internal libraries and modules.

Thanks @abierbaum !! Great job on that PR!

☠️ TSLint's time has come! ☠️

TSLint is no longer accepting most feature requests per #4534. See typescript-eslint.io for the new, shiny way to lint your TypeScript code with ESLint. ✨

It was a pleasure open sourcing with you all!

🤖 Beep boop! 👉 TSLint is deprecated 👈 _(#4534)_ and you should switch to typescript-eslint! 🤖

🔒 This issue is being locked to prevent further unnecessary discussions. Thank you! 👋

Was this page helpful?
0 / 5 - 0 ratings