8000 [no-unsafe-assignment] [no-unsafe-call] [no-unsafe-return] Improve user experience when the “real” issue is caught by TypeScript · Issue #2665 · typescript-eslint/typescript-eslint · GitHub
[go: up one dir, main page]

Skip to content

[no-unsafe-assignment] [no-unsafe-call] [no-unsafe-return] Improve user experience when the “real” issue is caught by TypeScript #2665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 tasks done
lydell opened this issue Oct 11, 2020 · 7 comments
Labels
package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on

Comments

@lydell
Copy link
Contributor
lydell commented Oct 11, 2020
  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have read the FAQ and my problem is not listed.

Repro

{
  "rules": {
    "@typescript-eslint/no-unsafe-assignment": "error",
    "@typescript-eslint/no-unsafe-call": "error",
    "@typescript-eslint/no-unsafe-return": "error",
  }
}
function run(tools: Array<string>): number {
  const tool = findMatchingTool(tools, "1.0")
  return getScore(tool);
}
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "importsNotUsedAsValues": "error",
    "isolatedModules": true,
    "module": "CommonJS",
    "moduleResolution": "node",
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "ES2019",
    "lib": [
      "ES2019",
    ],
    "outDir": "build"
  }
}

Expected Result

const tool = line: I only want TypeScript to say that findMatchingTool isn’t defined.

return line: I only want TypeScript to say that getScore isn’t defined.

Actual Result

ESLint:

/Users/lydell/test-project/tools.ts
  2:9   error  Unsafe assignment of an any value    @typescript-eslint/no-unsafe-assignment
  2:16  error  Unsafe call of an any typed value    @typescript-eslint/no-unsafe-call
  3:3   error  Unsafe return of an any typed value  @typescript-eslint/no-unsafe-return
  3:10  error  Unsafe call of an any typed value    @typescript-eslint/no-unsafe-call

TypeScript:

tools.ts:2:16 - error TS2304: Cannot find name 'findMatchingTool'.

2   const tool = findMatchingTool(tools, "1.0");
                 ~~~~~~~~~~~~~~~~

tools.ts:3:10 - error TS2304: Cannot find name 'getScore'.

3   return getScore(tool);
           ~~~~~~~~

The TypeScript errors are enough! Once they are fixed the no-unsafe-* errors are likely fixed as well!

Additional Info

Although I showed CLI output above for brevity, my issue is about the experience in VSCode (or any editor with ESLint support really).

I often write code that calls nonexistent functions. Once I’ve sketched out the code a bit I go back and try to actually implement those functions. I do this by jumping to the next error – sometimes I end up fixing something else, like a typo, and eventually I come across one of those functions that don’t exist yet. At first I might not remember that the function does not exist, so I read the error messages to find out what’s wrong. That can be a bit noisy. The real error is that the function does not exist, but it gets lost in a sea of “unsafe any” errors.

When I see those “unsafe any” errors I think like “Duh, of course they’d be any, the function does not even exist”.

I think it would be awesome if the no-unsafe-* rules could avoid causing errors if there’s another TypeScript error causing them in the first place!

My goal is to improve the editing experience by reducing unnecessary errors/warnings that clutter the editor. If you have a better idea on how to do that I’d be all ears!

Possibly related: #2164

Versions

package version
@typescript-eslint/eslint-plugin 4.4.0
@typescript-eslint/parser 4.4.0
TypeScript 4.0.3
ESLint 7.11.0
node 14.10.1
@lydell lydell added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Oct 11, 2020
@bradzacher
Copy link
Member

This is not really feasible to solve.

TypeScript doesn't tell us that this thing resolves to nothing - instead it just tells us its type is any.

The anys are special "error" anys, but they just tell us there was an error resolving the type - not what the error was. We can't just ignore these either, because often these error anys indicate a configuration mismatch (I.e. Our ts instance wasn't able to find a type but your IDE/tsc instance was).

In order to figure out if an any is actually a "not found" error, we'd have to ask typescript to compute the diagnostics.

Computing the diagnostics can take an average of 50ms per file. That might not sound like much, but that adds up to 1 second per 20 files! Across an entire project this kills lint times (I learned this the hard way with the no-unused-vars-experimental rule).

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed triage Waiting for team members to take a look labels Oct 11, 2020
@lydell
Copy link
Contributor Author
lydell commented Oct 11, 2020

Sad to hear!

TypeScript + ESLint in VSCode is such a super nice way to write code. It’s a shame that they don’t feel more integrated, especially in the editor (on the command line one can just run tsc && eslint .).

On the same topic: microsoft/vscode-eslint#1087.

Is there any heuristic we could use to catch maybe 80% of the cases? For example, for no-unsafe-call – if the called thing is a variable name and that name isn’t in scope then we could avoid creating an error?

Alternatively, would it be possible to talk to the TypeScript developers and ask them to return better data? Sounds reasonable to be able to differentiate between “error any” and “actual any”.

@bradzacher
Copy link
Member

Yeah unfortunately there's a small issue with how IDEs are designed now a days.

Everything is an extension, and everything is separate.

TS support is provided using (an inbuilt) extension. ESLint support is provided using (a 3rd party) extension.
These two extensions run separately and cannot share anything.

In an ideal world we would be able to talk to the TS language server that's providing types to the IDE, and build on top of it to save time and effort.
In that world we would be able to short-circuit the lint run entirely when TS reports a parsing error.
In that world we would be able to consume diagnostics for free, as they've ready been computed for the IDE.

But alas... That's not how it works (for various reasons like security).

There is actually some work that someone else has done that might be of interest to you. It integrates eslint into typescript as a language server plugin.
https://github.com/Quramy/typescript-eslint-language-service

So why not promote this as the defacto way to use our tooling? Mainly because the ESLint extension is more prevelant and requires less config to get working.


Is there any heuristic we could use to catch maybe 80% of the cases? For example, for no-unsafe-call – if the called thing is a variable name and that name isn’t in scope then we could avoid creating an error?

The problem is that we don't know if you were referring to a global thing that doesn't exist because you've misconfigured something.

There's probably something we can do, but my intuition makes me thing that it's likely that most (if not all) cases of false-po 8000 sitives have a similar misconfiguration possibility.

With everything to do with linting, I always favor false-positives to false-negatives. False positives are noise you can ignore (annoying people is bad sure, but better than...). False negatives unknowingly introduce bugs and erode the user's trust in the ecosystem.


It's currently possible for us to detect error anys using private properties on the type, but that's all we can tell - that it's an error any.

There's probably more TS could report here without requiring us to reach for diagnostics, but I just haven't invested too much time into thinking about it. It'd need some thinking to provide a decent proposal to the TS team, and possibly more time to implement into TS itself.

Time is something I don't have enough of unfortunately 😅

@lydell
Copy link
Contributor Author
lydell commented Oct 12, 2020

Thanks for the detailed reply!

I think the “correct” solution to this problem is more strict options in TypeScript itself. It’s a bit odd IMO that you need a separate tool to find lurking any when there’s noImplicityAny. The rules that overlap with the compiler should be in the compiler. Then the separation between tools isn’t so bad anymore.

I’m currently diving into the TypeScript issue tracker to see if something like this has been discussed before.

@lydell
Copy link
Contributor Author
lydell commented Oct 16, 2020

I think we’re doomed:

Feel free to close the issue if it helps maintenance on this repo, or keep it open if you feel like it’s a legit thing to improve although unlikely to be fixed.

@ark120202
Copy link

Because of https://gist.github.com/RyanCavanaugh/f80f9ddc50d45c4d76e7c4101efada28 it's is actually quite likely to be added. There is microsoft/TypeScript#40178, though for now it covers only property access.

@bradzacher
Copy link
Member

As there's nothing we can strictly action in this project right now - I'm going to close this.

@bradzacher bradzacher added wontfix This will not be worked on and removed awaiting response Issues waiting for a reply from the OP or another party labels Jan 8, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

3 participants
0