diff --git a/basedtyping/__init__.py b/basedtyping/__init__.py index 1ec8a0a..d2c11cd 100644 --- a/basedtyping/__init__.py +++ b/basedtyping/__init__.py @@ -51,6 +51,7 @@ "Untyped", "Intersection", "TypeForm", + "SpecialForm", ) if TYPE_CHECKING: @@ -555,4 +556,30 @@ def __getitem__(self, parameters: object | tuple[object]) -> _BasedGenericAlias: def f[T](t: TypeForm[T]) -> T: ... reveal_type(f(int | str)) # int | str + reveal_type(f(int)) # int + """) + +class _SpecialFormForm(_BasedSpecialForm, _root=True): # type: ignore[misc] + def __init__(self, doc: str): + self._name = "SpecialForm" + self._doc = self.__doc__ = doc + + def __getitem__(self, parameters: object | tuple[object]) -> _BasedGenericAlias: + if not isinstance(parameters, tuple): + parameters = (parameters,) + + return _BasedGenericAlias(self, parameters) # type: ignore[arg-type] + + +SpecialForm = _SpecialForm(doc="""\ + A type that can be used to represent a ``SpecialForm``. + For example: + + reveal_type(int) # type[int] + reveal_type(int | str) # SpecialForm[int | str] + + def f[T](t: SpecialForm[T]) -> T: ... + + reveal_type(f(int | str)) # int | str + reveal_type(f(int)) # error """) diff --git a/tests/test_specialform.py b/tests/test_specialform.py new file mode 100644 index 0000000..4152d1d --- /dev/null +++ b/tests/test_specialform.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from basedtyping import SpecialForm + + +class A: + x: int + + +class B: + y: int + + +def test_typeform(): + assert str(SpecialForm[Union[A, B]]) == f"basedtyping.TypeForm[Union[{A.__module__}.{A.__qualname__}, {B.__module__}.{B.__qualname__}]]"