Skip to content

Support typing.Union/typing.Optional in isinstance check (/support old-style unions) #17680

@bram-tv

Description

@bram-tv

Preamble

Feature

Example code

from typing import Any, Union, Optional

foo: Any = None

if isinstance(foo, int | None):
    reveal_type(foo)

if isinstance(foo, Union[int, None]):
    reveal_type(foo)

if isinstance(foo, Optional[int]):
    reveal_type(foo)

bar = int | None
baz = Optional[int]
if isinstance(foo, bar):
    reveal_type(foo)

if isinstance(foo, baz):
    reveal_type(foo)

Output for the above with mypy v1.11.1:

src/union.py:6: note: Revealed type is "Union[builtins.int, None]"
src/union.py:8: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:9: note: Revealed type is "Any"
src/union.py:11: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:12: note: Revealed type is "Any"
src/union.py:17: note: Revealed type is "Union[builtins.int, None]"
src/union.py:19: error: Parameterized generics cannot be used with class or instance checks  [misc]
src/union.py:19: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:20: note: Revealed type is "Any"
Found 4 errors in 1 file (checked 1 source file)

Running the above with pyright:

.../src/union.py
  .../src/union.py:6:17 - information: Type of "foo" is "int | None"
  .../src/union.py:9:17 - information: Type of "foo" is "int | None"
  .../src/union.py:12:17 - information: Type of "foo" is "int | None"
  .../src/union.py:17:17 - information: Type of "foo" is "int | None"
  .../src/union.py:20:17 - information: Type of "foo" is "int | None"
0 errors, 0 warnings, 5 informations

Summary:

  • when using the PEP 604-syntax the type is correctly narrowed to Union[builtins.int, None]
  • when using the typing.Union and/or typing.Optional syntax the type is not narrowed and remains Any (and errors are shown)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions