TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Incorrect ts7022 error emitted by tsserver (not by tsc)

Open martaver opened this issue 4 months ago • 10 comments

🔎 Search Terms

incorrect ts7022, wrong ts7022, tsserver only error ts7022

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about 'Type System Behavior'.

Summary: tsserver emits incorrect TS7022 error, meanwhile tsc and TS Playground behave as expected.

Steps to Reproduce (NOTE: the error is NOT reproducible in TS Playground):

  1. Clone https://github.com/cleric-sh/repro/tree/phantom-ts7022-error-in-vscode
  2. Ensure switched to branch phantom-ts7022-error-in-vscode
  3. npm i
  4. npm run compile --> observe no compiler errors
  5. Open index.ts (in VSCode or another editor utilizing tsserver)
  6. Error may or may not appear on line 26 on value (depending on whether analysis is on first pass or not)
  7. Make a change to the file (e.g. add a space) to trigger tsserver to re-analyse the file.
  8. Error should definitely be visible now.
  9. Restart the TS Language Server and error disappears. Make a change, and error re-appears.

The code in this repro emits an error that is only visible in VSCode whose intellisense is server by tsserver.

To compare, the same error is not emitted by tsc for the same source and configuration.

I have attempted to raise this issue at:

  • https://github.com/microsoft/vscode/issues/195075, and
  • https://github.com/typescript-language-server/typescript-language-server/issues/766

In typescript-language-server I was informed that the error is actually produced in tsserver and so I should raise this with the typescript team, so here I am!

⏯ Playground Link

https://tsplay.dev/WkqM2N

💻 Code

function Builder<I>(def: I) {

  return def;
}

interface IThing {
  doThing: (args: { value: object }) => string
  doAnotherThing: () => void
}

Builder<IThing>({

  doThing(args: { value: object }) {
    /**
     * ts7022 is shown on 'value' below.
     * 
     * Only shown when func param 'arg' type is declared. Removing the declaration resolves the error.
     * Only shown in vscode. tsc compiles without errors.
     * Only shown on second pass, after a change has been made to the file. Initial type check on load shows no errors.
     * Only shown when noImplicitAny: true
     * 
     * In a more complex file, declaring this function
     * after 'doAnotherThing' prevents the error being
     * displayed.
     */
    const { value } = this.args
    return `${value}`
  },

  doAnotherThing() { },  

})

🙁 Actual behavior

Error is shown on Line 26:

'value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)

🙂 Expected behavior

Behaviour in tsserver to match tsc (which does not emit the error).

Not sure exactly what should happen.... I would imagine one of:

  • this resolves to any, since ThisType isn't used, and thus value should also be any.
  • this resolve to IThing.

But the ts7022 error shouldn't be displayed.

Additional information about the issue

The error appears with all extensions disabled.

The error appears with all versions of typescript (I have checked all versions since 5.2.2), including 5.4.0-beta.

The error does NOT appear in TS Playground: https://tsplay.dev/WkqM2N

I've noted that the error is:

  • Only shown when func param 'arg' type is declared. Removing the declaration resolves the error.
  • Only shown in vscode. tsc compiles without errors.
  • Only shown on second pass, after a change has been made to the file. Initial type check on load shows no errors.
  • Only shown when noImplicitAny: true

Also, in a more complex file, declaring this function after doAnotherThing prevents the error being displayed.

There is generally a lot of strange behaviour in this scenario of using 'this' in a non-class context. The repro is non-sensical, but is the simplest reproduction of the same error I could find.

At least this repro demonstrates:

  • the error doesn't seem to make sense. e.g. why wouldn't value just be any since this isn't typed?
  • the error is inconsistent:
    • disappearing if (for example) the returned object from Builder is assigned to a variable.
    • not emitted by TSC or visible in TS Playground

martaver avatar Feb 17 '24 11:02 martaver

I was not able to reproduce this using the provided repository.

Andarist avatar Feb 19 '24 08:02 Andarist

Just checking that you're noting: The error only appears on subsequent analysis of the file. The first analysis (and compiling with tsc) will NOT emit the error.

Did you try editing the file to re-trigger analysis by tsserver...?

The error is consistently reproducible for me on every machine I have on a fresh clone. image

If you don't see the error, then it might related to the environment (which is scary)?

  • I'm on an M2 Macbook Pro, on MacOS Sonoma
  • VSCode Version: 1.86.2
  • typescript 5.4.0-beta
  • npm 10.2.4

But I've been able to reproduce this error for months, on many permutations of machine, TS version and node version.

Could you please take another look?

martaver avatar Feb 19 '24 08:02 martaver

Yes, I tried all of this. I've gone through VS Code update just now and that didn't help me to reproduce this either. Have you gone through "Select TypeScript version..." in the command palette (although I can't repro with neither workspace/vscode version)?

Andarist avatar Feb 19 '24 09:02 Andarist

Yeah... switching between VSCode and Workspace typescript versions doesn't change anything - the error appears either way.

This is really interesting... I wonder what could be causing this.

Also all extensions disabled...

The error is displayed on any version of TS I try so far (anything above 5.2 so far), regardless of VSCode version and my machine.

I'm always on MacOS (but also appears predictably on different versions of MacOS).

What OS are you on?

martaver avatar Feb 19 '24 11:02 martaver

MacOS Monterey - I really doubt that this matters at all though. I could hop on a call someday with you to do some pair debugging if you'd be willing to spend some evening on this 😉

Andarist avatar Feb 19 '24 13:02 Andarist

I can't get this to repro either, but the fact that it does intermittently repro does sort of make sense. Circularity detection can be path-dependent and the language service can query things in different order depending on user settings (for example, computing semantic highlighting). In theory there's some way to consistently repro this in an automated test (e.g. fourslash) and that's really what we'd need to investigate further.

RyanCavanaugh avatar Feb 22 '24 18:02 RyanCavanaugh

I would happy to jump in a help debug this. It would be pretty fun to determine that it was my colour theme in VSCode that triggered a bug in tsserver :D Also interested in how to debug TS in such a way.

@RyanCavanaugh in your opinion, is the message I am describing erroneous? To my best understanding, I don't see how there can be any circularity in the code I have posted.

I am in Helsinki, so my time is UTC+2. Should we take this to another channel? How do you guys normally communicate outside of GH issues?

martaver avatar Feb 23 '24 08:02 martaver

@martaver feel free to DM me on Twitter ( https://twitter.com/AndaristRake ) or on Discord (my nickname is Andarist)

Andarist avatar Feb 23 '24 09:02 Andarist

I've never seen a circularity error issued where there wasn't some circularity. Sometimes it's subtle to notice, but it's always there.

RyanCavanaugh avatar Feb 23 '24 16:02 RyanCavanaugh

To keep note, I think the fact that error is only present when the type of arg in doThing is explicitly declared is telling.

When no type is explicitly declared for the method's arg, Typescript defers to IThing for the method signature's types. In this case, this is simply always IThing.

When the type is explicitly declared, then Typescript obtains the method's signature's types from the implementation. In this case, it's not clear what the type of doThing's params should be on this. Should it be as declared in IThing, or the local, explicit declaration?

In this case, Typescript seems to be considering the local, explicit declarations, and since doStuff returns value obtained from this, the return type of doStuff therefore depends on the type of this, which in turn depends on the return type of doStuff...

That must be the circularity.

So since the problem only shows when noImplicitAny is enabled, I'm guess Typescript must be looking at value and trying to infer a narrower type than any. In doing so, it needs to inspect the type of this.args, and so falls into the circularity.

This explains why disabling noImplicitAny prevents the error, because it prevents TS from inspecting this.

However, this still doesn't explain why it only occurs on the second pass.

I've tried switching off all extensions and even switching Color Theme now...

Going to reach out to @Andarist to dive deeper!

martaver avatar Feb 24 '24 10:02 martaver

We dug into this over the call and it turns out that this is the very same issue as the one that I diagnosed recently here. An extra failing test case for this issue can be found here (1 error is expected but today we get 2 instead):

/// <reference path="fourslash.ts" />

// @strict: true

//// function Builder<I>(def: I) {
////   return def;
//// }
////
//// interface IThing {
////   doThing: (args: { value: object }) => string
////   doAnotherThing: () => void
//// }
////
//// Builder<IThing>({
////   doThing(args: { value: object }) {
////     const { v/*1*/alue } = this.[|args|]
////     return `${value}`
////   },
////   doAnotherThing() { },
//// })

verify.quickInfoAt("1", "const value: any");
verify.getSemanticDiagnostics([{
  message: "Property 'args' does not exist on type 'IThing'.",
  code: 2339,
}]);

You can actually repro it in the playground but you have to quickly hover over value in the binding pattern after making an edit.

Andarist avatar Mar 06 '24 15:03 Andarist

I suppose I'll close this issue in favour of #57585 and track it there.

martaver avatar Mar 08 '24 12:03 martaver