pythonoverloadingpython-typing

VS Code selects incorrect overload when a function parameter has a default value


I'm overloading a method so the return type differs depending on the value of a given bool parameter. That same parameter has a default value (False in my case).

Here's a simplistic example function to demonstrate:

import typing

@typing.overload
def fn(val: int, to_none: typing.Literal[True] = ...) -> None: ...

@typing.overload
def fn(val: int, to_none: typing.Literal[False] = ...) -> float: ...

@typing.overload
def fn(val: int) -> float: ...

def fn(val: int, to_none: bool = False) -> float | None:
    return None if to_none else float(val)

x = fn(5, to_none=True)

y = fn(6, to_none=False)

z = fn(7)

My problem is with z. VS Code doesn't appear to take the default value of the parameter into account and considers it a None as if True had been passed into the to_none parameter:

Screenshot of VS Code with a popup containing details about variable z which it considers to be a None type

Is this a problem with my overload definition, or a bug with VS Code/pylance/etc.?


Solution

  • The overload definition of the True case is wrong: the ellipsis (...) after to_none should not be given here. That is, replace the stub with:

    @typing.overload
    def fn(val: int, to_none: typing.Literal[True]) -> None: ...
    

    … and z should be inferred as float correctly.

    Why is this? I quote my earlier answer to a related question, regarding the use of ellipses as placeholders for default values in stubs:

    In the way the ellipses are used, they indicate that a default value is actually part of this particular overload.

    In your True case, the default value (False) is not part of this particular overload (to_none: typing.Literal[True]), so no ellipsis should be written there.