Skip to content

infer x: T if x.__class__: type[T] #1163

@jorenham

Description

@jorenham

Describe the Bug

So if e.g. x.__class__ is int, then narrow x: int:

from typing import reveal_type

def g(x):
    assert x.__class__ is int
    reveal_type(x)  # revealed type: Unknown (_.__class__: type[int])

(sandbox)


Here's a structural use-case:

from typing import Protocol, final

@final
class JustInt(Protocol):
    @property
    def __class__(self, /) -> type[int]: ...
    @__class__.setter
    def __class__(self, t: type[int], /) -> None: ...

def assert_int(x: JustInt, /) -> int:
    assert isinstance(x, int)  # should not be needed
    return x

assert_int(42)     # correctly accepted
assert_int(False)  # correctly rejected ;)

(sandbox)


Another interesting use-case is emulating the intersection of a structural and a nominal type:

from typing import Protocol, final

class Thing: ...
class IntThing(Thing):
    def __int__(self) -> int: return 42
class FloatThing(Thing):
    def __float__(self) -> float: return 42

@final
class ThingThatSupportsFloat(Protocol):
    @property
    def __class__(self, /) -> type[Thing]: ...
    def __float__(self, /) -> float: ...

So this would make ThingThatSupportsFloat equivalent to Thing & typing.SupportsFloat. That should take care of many (if not most) of the intersection-type use-cases, for free basically.

Sandbox Link

No response

(Only applicable for extension issues) IDE Information

No response

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions