Describe the bug
In some cases, a valid order of instance-method overloads will be (wrongly) rejected by pyright/pylance, but only if the implementing class inherits from a class that defines some of the overloads.
For example (see below for a complete source code including all imports etc) the following two classes are type hinted correctly (as expected):
class ValidBase:
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: Literal[True]) -> Tuple[str]|Tuple[int]: ...
class ValidComplete:
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ...
Notice, that the two last overloads in ValidComplete are the same as in ValidBase and that every object inheriting from ValidComplete also completely matches the interface defined in ValidBase.
However, the following class, although being a 1:1 copy of ValidComplete (+ inheriting from ValidBase) and having the exact same (mutually exclusive) overloads on the map(...) method as ValidBase will show a type error:
class InvalidDerived(ValidBase):
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ... # -> type error saying that "map(...)" overrides "ValidBase" in an incompatible manner
Finally, if the two overload blocks are switched in the derived class, the type error disappears:
class ValidDerived(ValidBase):
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ... # -> no type error?
I think the following behaviours are wrong here:
map(...) on InvalidDerived should not be marked as invalid by pyright
- The order between these mutually exclusive overloads should not matter, so if the behaviour of
InvalidDerived is intended to be a type error, then ValidDerived should also be marked as invalid
Code or Screenshots
from typing import Literal, Tuple, overload
# works as expected:
class ValidBase:
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: Literal[True]) -> Tuple[str]|Tuple[int]: ...
# works as expected
class ValidComplete:
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ...
# does not work, this is the issue i am reporting:
class InvalidDerived(ValidBase):
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ... # -> type error saying that "map(...)" overrides "ValidBase" in an incompatible manner
# works, but i dont understand why the order between the overloads would matter?
class ValidDerived(ValidBase):
@overload
def map(self, x: str, wrap: Literal[True]) -> Tuple[str]: ...
@overload
def map(self, x: int, wrap: Literal[True]) -> Tuple[int]: ...
@overload
def map(self, x: str, wrap: Literal[False]=False) -> str: ...
@overload
def map(self, x: int, wrap: Literal[False]=False) -> int: ...
def map(self, x: str|int, wrap: bool=False) -> str|int|Tuple[str]|Tuple[int]: ... # -> no type error?
(roughly translates to)
The method "map" overrides the class "ValidBase" in an incompatible manner.
The override does not handle all overloads of the base method.
Pylance(reportIncompatibleMethodOverride)
Untitled-6(10, 9): Overriden method
VS Code extension or command-line
The screenshot was made in VSCode (1.127.0 Universal) with the latest pylance Plugin (2026.2.1) installed.
Describe the bug
In some cases, a valid order of instance-method overloads will be (wrongly) rejected by pyright/pylance, but only if the implementing class inherits from a class that defines some of the overloads.
For example (see below for a complete source code including all imports etc) the following two classes are type hinted correctly (as expected):
Notice, that the two last overloads in
ValidCompleteare the same as inValidBaseand that every object inheriting fromValidCompletealso completely matches the interface defined inValidBase.However, the following class, although being a 1:1 copy of
ValidComplete(+ inheriting fromValidBase) and having the exact same (mutually exclusive) overloads on themap(...)method asValidBasewill show a type error:Finally, if the two overload blocks are switched in the derived class, the type error disappears:
I think the following behaviours are wrong here:
map(...)onInvalidDerivedshould not be marked as invalid by pyrightInvalidDerivedis intended to be a type error, thenValidDerivedshould also be marked as invalidCode or Screenshots
(roughly translates to)
VS Code extension or command-line
The screenshot was made in VSCode (1.127.0 Universal) with the latest pylance Plugin (2026.2.1) installed.