-
Notifications
You must be signed in to change notification settings - Fork 216
Description
Consider the following example:
from typing import TypeVar, Any
type IntOr[T: int] = int | T
def _(x: IntOr[Any]):
reveal_type(x)ty reveals int here, but every other type checker reveals int | Any, which seems more reasonable? ty eagerly simplifies the int | T union to int, which is correct for every static T <: int... but leads to surprising results when T is explicitly specialized to a dynamic type.
This was prompted by a much more complex example in numpy's codebase:
class _SupportsDType(Protocol[_DTypeT_co]):
@property
def dtype(self) -> _DTypeT_co: ...
_DTypeLike = type[_ScalarT] | _SupportsDType[dtype[_ScalarT]]_ScalarT has an upper bound of np.generic (= np.generic[Any]). And np.generic[…] does have a dtype property member. This currently leads us to treat type[_Scalar] as a subtype of _SupportsDType[…], and so that union gets simplified to _SupportsDType[dtype[_ScalarT]]. Later, _DTypeLike is explicitly specialized with _DTypeLike[Any], and it is expected that <class 'object'> should be assignable to _DTypeLike[Any] (which would be the case if type[Any] were still part of the union.