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

Why are type annotations sometimes ignored in favor of Pylance's inferred types (in particular, Unknown)? #6589

Closed
mattdeutsch opened this issue Oct 22, 2024 · 1 comment
Assignees
Labels
needs repro Issue has not been reproduced yet

Comments

@mattdeutsch
Copy link

mattdeutsch commented Oct 22, 2024

Here's a minimal recreation:

def fn(naked_dict: dict):
    s: str | None = naked_dict.get("key")
    # mousing over s reveals it has type "Unknown | None"
    # and no syntax highlighting is given for string methods on s.
    s.upper()
    # even after removing None as a possibility, no syntax highlighting is given:
    if s:
        s.upper()

From what I can gather, Pylance is typing naked_dict as dict[Unknown, Unknown], and inferring that s must be of type Unknown or None because of the signature of dict.get. That's clearly WAI.

What's surprising to me is that Pylance ignores my type annotation of str | None. I would expect that in cases where I have more information than the type system, that I am able to tell it what I know about the system (in this case, I know that the key "key" in naked_dict, when present, always points to a string). Why does Pylance fight with me on this and claim that s is of type Unknown | None?

I'm quite confident that this is just me misunderstanding the goals of Pylance as a project, so I'm unwilling to file this as a bug. But I would like to understand why this choice has been made, and appreciate the time of any contributors willing to help me understand. Is it a problem with the LSP?

Thanks!

@github-actions github-actions bot added the needs repro Issue has not been reproduced yet label Oct 22, 2024
@erictraut
Copy link
Contributor

I recommend reading this documentation to better understand the core concepts for static typing.

You should think of a type annotation as a declaration. The statement s: str | None declares that any value assigned to variable s must conform with the type str | None. It is the job of a static type checker to enforce such declarations. The Python typing spec provides assignability rules that dictate which types are assignable to other types. For example, a value of type Literal["hi"] is assignable to a variable that is declared to accept values of type str | None, but a value of type Literal[1] is not assignable.

When a value is assigned to a variable, a static type checker will track its locally-narrowed type. This type may be narrower (more specific) than the variable's declared type.

def func(x: str | None):
    reveal_type(x) # str | None
    x = "hi"
    reveal_type(x) # Literal["hi"]
    x = None
    reveal_type(x) # None
    x = 1 # Type violation

@rchiodo rchiodo closed this as completed Oct 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs repro Issue has not been reproduced yet
Projects
None yet
Development

No branches or pull requests

4 participants