GitHub

This repository wraps an issue I've had with writing correct types when using PEP-696 defaults in typing.TypeVar.

The goal is to have a function from_record() that knows to make a base class by default, but also allows for passing derived classes from the base class.

It should know if you pass a class to element_cls, to dispatch to creating that class and then should be smart enough to narrow the return type. If no element_cls is returned, then it shouldn't narrow the return type, and instead just use the default.

REC = {"a": "B"}
typing.reveal_type(from_record(REC))  # reveals Element, since this is default
typing.reveal_type(from_record(REC, element_cls=Element))  # reveals Element
typing.reveal_type(from_record(REC, element_cls=DerivedElement))  # reveals DerivedElement

The problem, is how to type element_cls? I am writing this because I hope someone can help! I have tried using variations on how T is defined, defining other variants of T that include type[Element] inside, and using overloads, but nothing worked so far.

The example can be found in main.py, and reproduced with MyPy (on Python 3.13+, because of when PEP-696 was added to stdlib) with the following commands:

$ git clone https://github.com/cthoyt/python-typing-dilemma
$ cd python-typing-dilemma
$ uvx --python 3.13 mypy --strict main.py
main.py:23: error: Incompatible default for argument "element_cls" (default has type "type[Element]", argument has type "type[T]")  [assignment]
main.py:29: note: Revealed type is "main.Element"
main.py:30: note: Revealed type is "main.Element"
main.py:31: note: Revealed type is "main.DerivedElement"

^ I omitted the MyPy output from the second attempt I made. If you get as far as reproducing it, you'll see a totally different error type.

License

MIT License

Read the original on github.com ↗