-
Notifications
You must be signed in to change notification settings - Fork 288
-
The following causes a runtime error without any type-checker warning:
from typing import overload @overload def f(arg: int, /) -> int: ... @overload def f(arg: str, /) -> str: ... def f(arg: int | str, /) -> int | str | bytes: return b"" f(1) + 1
Looking at the typing specs on Implementation consistency,
... the return type of all overloads should be assignable to the return type of the implementation.
this doesn't seem quite right, because a broader return type in the implementation will almost always cause runtime issues in the calling context or above, while a narrower return type in the implementation (coupled with overload consistency checks in the signature) should never cause type errors in the calling context:
@overload def f(arg: int, /) -> int: ... # Pyright and Pyrefly complain of the following overload and implementation, # but it seems perfectly type-safe. @overload def f(arg: int | str, /) -> int | str: ... def f(arg: int | str, /) -> int: return 1
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment
-
I think you're right.
The check that all overload return types are assignable to the implementation return type is not really necessary for soundness, but it is a useful consistency check, since it's probably a mistake to annotate an overload as returning a type outside what the implementation can return.
I think ideally we would also add a check that the implementation return type be assignable to the union of the overload return types -- that check is actually needed for soundness.
Beta Was this translation helpful? Give feedback.