Skip to content

Commit faac780

Browse files
authored
Support type aliases, NamedTuple and TypedDict in constrained TypeVar defaults (#18884)
Fixes #18862. Fixes #17686.
1 parent b147d11 commit faac780

File tree

5 files changed

+143
-4
lines changed

5 files changed

+143
-4
lines changed

mypy/checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2664,7 +2664,7 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None:
26642664
continue
26652665
if not is_subtype(tv.default, tv.upper_bound):
26662666
self.fail("TypeVar default must be a subtype of the bound type", tv)
2667-
if tv.values and not any(tv.default == value for value in tv.values):
2667+
if tv.values and not any(is_same_type(tv.default, value) for value in tv.values):
26682668
self.fail("TypeVar default must be one of the constraint types", tv)
26692669

26702670
def check_enum(self, defn: ClassDef) -> None:

mypy/checkexpr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6171,7 +6171,7 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
61716171
):
61726172
if not is_subtype(p_default, e.upper_bound):
61736173
self.chk.fail("TypeVar default must be a subtype of the bound type", e)
6174-
if e.values and not any(p_default == value for value in e.values):
6174+
if e.values and not any(is_same_type(p_default, value) for value in e.values):
61756175
self.chk.fail("TypeVar default must be one of the constraint types", e)
61766176
return AnyType(TypeOfAny.special_form)
61776177

test-data/unit/check-python312.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,3 +2060,27 @@ class R:
20602060

20612061
class Action:
20622062
pass
2063+
2064+
[case testPEP695TypeVarConstraintsDefaultAliases]
2065+
from typing import Generic
2066+
from typing_extensions import TypeVar
2067+
2068+
type K = int
2069+
type V = int
2070+
type L = list[int]
2071+
2072+
T1 = TypeVar("T1", str, K, default=K)
2073+
T2 = TypeVar("T2", str, K, default=V)
2074+
T3 = TypeVar("T3", str, L, default=L)
2075+
2076+
class A1(Generic[T1]):
2077+
x: T1
2078+
class A2(Generic[T2]):
2079+
x: T2
2080+
class A3(Generic[T3]):
2081+
x: T3
2082+
2083+
reveal_type(A1().x) # N: Revealed type is "builtins.int"
2084+
reveal_type(A2().x) # N: Revealed type is "builtins.int"
2085+
reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]"
2086+
[builtins fixtures/tuple.pyi]

test-data/unit/check-python313.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,19 @@ def func_d1(
274274
reveal_type(d) # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
275275
[builtins fixtures/tuple.pyi]
276276
[typing fixtures/typing-full.pyi]
277+
278+
[case testTypeVarConstraintsDefaultAliasesInline]
279+
type K = int
280+
type V = int
281+
282+
class A1[T: (str, int) = K]:
283+
x: T
284+
class A2[T: (str, K) = K]:
285+
x: T
286+
class A3[T: (str, K) = V]:
287+
x: T
288+
289+
reveal_type(A1().x) # N: Revealed type is "builtins.int"
290+
reveal_type(A2().x) # N: Revealed type is "builtins.int"
291+
reveal_type(A3().x) # N: Revealed type is "builtins.int"
292+
[builtins fixtures/tuple.pyi]

test-data/unit/check-typevar-defaults.test

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,8 +729,6 @@ class C(Generic[_I]): pass
729729
t: type[C] | int = C
730730
[builtins fixtures/tuple.pyi]
731731

732-
733-
734732
[case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault]
735733
from typing_extensions import TypeVar
736734
from typing import Generic, Union
@@ -749,3 +747,104 @@ MyA = A[T1, int]
749747
a: MyA = A(None, 10)
750748
reveal_type(a.a) # N: Revealed type is "Union[builtins.int, None]"
751749
[builtins fixtures/tuple.pyi]
750+
751+
[case testTypeVarConstraintsDefaultAliasesTypeAliasType]
752+
from typing import Generic
753+
from typing_extensions import TypeAliasType, TypeVar
754+
755+
K = TypeAliasType("K", int)
756+
V = TypeAliasType("V", int)
757+
L = TypeAliasType("L", list[int])
758+
T1 = TypeVar("T1", str, K, default=K)
759+
T2 = TypeVar("T2", str, K, default=V)
760+
T3 = TypeVar("T3", str, L, default=L)
761+
762+
class A1(Generic[T1]):
763+
x: T1
764+
class A2(Generic[T2]):
765+
x: T2
766+
class A3(Generic[T3]):
767+
x: T3
768+
769+
reveal_type(A1().x) # N: Revealed type is "builtins.int"
770+
reveal_type(A2().x) # N: Revealed type is "builtins.int"
771+
reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]"
772+
[builtins fixtures/tuple.pyi]
773+
774+
[case testTypeVarConstraintsDefaultAliasesImplicitAlias]
775+
from typing_extensions import TypeVar
776+
777+
K = int
778+
V = int
779+
L = list[int]
780+
T1 = TypeVar("T1", str, K, default=K)
781+
T2 = TypeVar("T2", str, K, default=V)
782+
T3 = TypeVar("T3", str, L, default=L)
783+
[builtins fixtures/tuple.pyi]
784+
785+
[case testTypeVarConstraintsDefaultAliasesExplicitAlias]
786+
from typing_extensions import TypeAlias, TypeVar
787+
788+
K: TypeAlias = int
789+
V: TypeAlias = int
790+
L: TypeAlias = list[int]
791+
T1 = TypeVar("T1", str, K, default=K)
792+
T2 = TypeVar("T2", str, K, default=V)
793+
T3 = TypeVar("T3", str, L, default=L)
794+
[builtins fixtures/tuple.pyi]
795+
796+
[case testTypeVarConstraintsDefaultSpecialTypes]
797+
from typing import Generic, NamedTuple
798+
from typing_extensions import TypedDict, TypeVar
799+
800+
class TD(TypedDict):
801+
foo: str
802+
803+
class NT(NamedTuple):
804+
foo: str
805+
806+
T1 = TypeVar("T1", str, TD, default=TD)
807+
T2 = TypeVar("T2", str, NT, default=NT)
808+
809+
class A1(Generic[T1]):
810+
x: T1
811+
class A2(Generic[T2]):
812+
x: T2
813+
814+
reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.str})"
815+
reveal_type(A2().x) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NT]"
816+
[builtins fixtures/tuple.pyi]
817+
818+
[case testTypeVarConstraintsDefaultSpecialTypesGeneric]
819+
from typing import Generic, NamedTuple
820+
from typing_extensions import TypedDict, TypeVar
821+
822+
T = TypeVar("T")
823+
824+
class TD(TypedDict, Generic[T]):
825+
foo: T
826+
class TD2(TD[int]): pass
827+
class TD3(TD[int]):
828+
bar: str
829+
830+
class NT(NamedTuple, Generic[T]):
831+
foo: T
832+
class NT2(NT[int]): pass
833+
834+
T1 = TypeVar("T1", str, TD[int], default=TD[int])
835+
T2 = TypeVar("T2", str, NT[int], default=NT[int])
836+
T3 = TypeVar("T3", str, TD2, default=TD[int])
837+
T4 = TypeVar("T4", str, TD3, default=TD[int]) # E: TypeVar default must be one of the constraint types
838+
T5 = TypeVar("T5", str, NT2, default=NT[int]) # E: TypeVar default must be one of the constraint types
839+
840+
class A1(Generic[T1]):
841+
x: T1
842+
class A2(Generic[T2]):
843+
x: T2
844+
class A3(Generic[T3]):
845+
x: T3
846+
847+
reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
848+
reveal_type(A2().x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NT[builtins.int]]"
849+
reveal_type(A3().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
850+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)