Skip to content
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

Incorrect hover information when initializing a union-typed const with never #59626

Closed
mon-jai opened this issue Aug 14, 2024 · 6 comments
Closed
Labels
Not a Defect This behavior is one of several equally-correct options

Comments

@mon-jai
Copy link

mon-jai commented Aug 14, 2024

πŸ”Ž Search Terms

"hover information", "union"

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.0-beta#code/PTAEAkHsDcFMCdQwaABgM0pVoDOALSAd1zQGNIA7XAF1E0gC48b4BLSgc1AB9RKArgFsARglQAoCtToNmACkGiUfWuy4BKUAF5+AgDb6AhBIkgIyRJbQAxLDgLFSqGgE8ADrFB3IO-rDh4STdPbyw-ENhIdHosCSA

πŸ’» Code

// Hover over `foo` shows `const foo: string | number`
const foo: (number | string) = null!

// Hover over `Foo` shows `type Foo = never`
type Foo = typeof foo

πŸ™ Actual behavior

Hover over foo shows const foo: string | number

πŸ™‚ Expected behavior

Hover over foo should show never, the same as type Foo.

Additional information about the issue

No response

@Andarist
Copy link
Contributor

The hover information is correct. This is subject to control flow analysis - despite the hover at the declaration (where you see the declared type) the actual type of the variable is an "assignment reduced type". This makes it possible to write code like this:

declare function takeString(str: string): void

type A = string | number

const test: A = 'foo'
takeString(test) // ok!

The type of null! is... never (after all it's like null & {}). At the moment, getAssignmentReducedType prefers assigned never over the declared type. I think (but I'm not sure) that it's mostly to handle a special kind of internal silentNeverType that is sometimes used by CFA.

There are 2 options/questions here that could be asked:

  1. should real never be preferred as the assigned type here? I have a local patch that doesn't show any meaningful changes when the declared type gets picked over it.
  2. could null! be contextually typed? If it could benefit from contextual typing then, likely, the problem would go away.
  3. is this even a problem? πŸ˜‰ If we look at it as a different way of as any for this specific situation then it feels like it could be treated as a problem. any is not preferred over the declared type in such assignments

@mon-jai
Copy link
Author

mon-jai commented Aug 14, 2024

I would vote for (1). If foo is narrowed, we should display the actual inferred type over the type declared with type hints.

This will make debugging easier. For example, the current behavior confused the guy behind vue-tsc, who should have fair enough experience with TypeScript: vuejs/language-tools#4682

For (3), that shouldn't be a problem since the actual inferred type is still number | string. (playground)

@Andarist
Copy link
Contributor

(1) I wasn't talking about what gets displayed in the tooltip but rather about what's the actual type coming out of this assignment.

The issue with the confusing tooltips is a known DX issue, I can't find the reference issue for it right now though.

@RyanCavanaugh RyanCavanaugh added the Not a Defect This behavior is one of several equally-correct options label Aug 15, 2024
@RyanCavanaugh
Copy link
Member

I don't see something that's more consistent than what we're already doing. Let's say you had this

declare const foo: "foo" | "bar";

switch (foo) {
    case "foo":
    case "bar":
        break;
    default:
        type T = typeof foo;
        foo satisfies never;
}

If foo satisfies never then T must be never too -- those operations are asking the same questions. Deviating for the specific case of never for display only would only confuse the situation when you're in a place where never is expected.

@Andarist
Copy link
Contributor

Yes, the hover info is fine - it's consistent with how it works everywhere. I just wonder if regular never should be preferred as the assigned type in this scenario since it's specific to declared types that are unions and I can see that tripping people. I wonder what exact benefit is in this:

declare const nothing: never;

const strOrNum: string | number = nothing;

strOrNum
// ^? const strOrNum: never

const str: string = nothing;

str
// ^? const str: string

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Not a Defect This behavior is one of several equally-correct options
Projects
None yet
Development

No branches or pull requests

4 participants