Can mypy track string literals?

Viewed 2191

Is there anyway to make this work

from typing import Literal
def foo(bar: Literal["bar"]) -> Literal["foo"]:
    foo = "foo"
    return foo


bar = "bar"
foo(bar)

Here are the errors

foo.py:4: error: Incompatible return value type (got "str", expected "Literal['foo']")
foo.py:8: error: Argument 1 to "foo" has incompatible type "str"; expected "Literal['bar']"

Is pretty obvious that foo variable and bar are literals because they are assigned to literals, so this is safe, but mypy seems to not track this. Is there anything that I'm missing?

1 Answers

MyPy infers literals to their builtin type, not a Literal of their value.

mypy Docs » Literal types

You must explicitly add an annotation to a variable to declare that it has a literal type. [..] variables without this annotation are not assumed to be literals.

To allow inference of Literal values, annotate the variable as Final:

from typing import Final

from typing_extensions import Final

bar: Final = "bar"
reveal_type(bar)  # Revealed type is 'Literal['bar']?'

Annotating a variable as Final indicates that its value will not be substituted for a value of similar type. This makes it correct to infer the type as the specific Literal value, instead of just the general type.

Note that this inference is contextual: The type is inferred as Literal for all cases where a Literal is expected. For cases in which a type is expected, be it the literal type, a base type or TypeVar, the type is inferred as the general type.

reveal_type([bar])  # Revealed type is 'builtins.list[builtins.str*]'
Related