Thanks for the clarifications. So casting is the solution to avoid this problem with static type checkers? Switching back to Literals... def f(x: Literal["A", "B", "C", 1, 2, 3]) -> None: if isinstance(x, int): g(cast(Literal[1, 2, 3], x)) else: ... def g(x: Literal[1, 2, 3]) -> None: ... On Sat, 2021-09-18 at 09:27 -0600, Carl Meyer wrote:
On Sat, Sep 18, 2021 at 9:13 AM Carl Meyer <carl@oddbird.net> wrote:
It is true that a Literal type will generally only be statically known when it originates from a Python literal expression. But this is just a specific case of the general limitations of static analysis: that statically-known types are a wider approximation of the actual runtime types.
To clarify this point: there are many other situations where the same symptom you observe (static type checking may fail where runtime checking would pass) can occur without the involvement of Literal types. Consider this example:
``` def f(x: object) -> None: g(x)
def g(x: int) -> None ...
f(1) ```
This code must fail static type checking because the call to g(x) is invalid since it passes an `object` where an `int` is required. The statically known type of `x` within `f` is only `object`, since call-sites of `f` will be allowed to pass any object. And yet in the specific case of the call `f(1)`, where the argument happens to be an integer, runtime type checking will be fine.
This is exactly analogous to the case you are describing, if we had instead `g(x: Literal[1]) -> None` and `f(x: int) -> None` -- this is statically a type error, because the static type check must consider that `x` could take on any value that is part of the type `int`, even though any given specific runtime call might check out fine, if `x` happens to in fact be `1` for that call.
Carl