Skip to content

Commit df3881f

Browse files
committed
Allow TypeVarTuple arguments to subclasses of generic TypedDict
1 parent 71942c0 commit df3881f

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

mypy/semanal_shared.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def anal_type(
182182
allow_tuple_literal: bool = False,
183183
allow_unbound_tvars: bool = False,
184184
allow_typed_dict_special_forms: bool = False,
185+
allow_unpack: bool = False,
185186
allow_placeholder: bool = False,
186187
report_invalid_types: bool = True,
187188
prohibit_self_type: str | None = None,

mypy/semanal_typeddict.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,16 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
253253

254254
for arg_expr in args:
255255
try:
256-
type = expr_to_unanalyzed_type(arg_expr, self.options, self.api.is_stub_file)
256+
type = expr_to_unanalyzed_type(
257+
arg_expr, self.options, self.api.is_stub_file, allow_unpack=True
258+
)
257259
except TypeTranslationError:
258260
self.fail("Invalid TypedDict type argument", ctx)
259261
return None
260262
analyzed = self.api.anal_type(
261263
type,
262264
allow_typed_dict_special_forms=True,
265+
allow_unpack=True,
263266
allow_placeholder=not self.api.is_func_scope(),
264267
)
265268
if analyzed is None:

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

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,116 @@ td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "C
11591159
[builtins fixtures/dict.pyi]
11601160
[typing fixtures/typing-typeddict.pyi]
11611161

1162+
[case testVariadicTypedDictDeriveUnpack]
1163+
from typing import Tuple, Callable, Generic, TypedDict, TypeVar
1164+
from typing_extensions import TypeVarTuple, Unpack
1165+
1166+
T = TypeVar("T")
1167+
Ts = TypeVarTuple("Ts")
1168+
class A(TypedDict, Generic[Unpack[Ts]]):
1169+
fn: Callable[[Unpack[Ts]], None]
1170+
1171+
class B(A[Unpack[Ts]]):
1172+
val: bool
1173+
1174+
class C(A[Unpack[Ts]], Generic[Unpack[Ts], T]):
1175+
val: T
1176+
1177+
1178+
y: B[int, str]
1179+
reveal_type(y) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1180+
reveal_type(y["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1181+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1182+
1183+
y2: C[int, str, bool]
1184+
reveal_type(y2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1185+
reveal_type(y2["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1186+
reveal_type(y2["val"]) # N: Revealed type is "builtins.bool"
1187+
1188+
z: B[Unpack[Tuple[int, ...]]]
1189+
reveal_type(z) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (*builtins.int), 'val': builtins.bool})"
1190+
reveal_type(z["fn"]) # N: Revealed type is "def (*builtins.int)"
1191+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1192+
1193+
z2: C[Unpack[Tuple[int, ...]]]
1194+
reveal_type(z2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.int})"
1195+
reveal_type(z2["fn"]) # N: Revealed type is "def (*builtins.int)"
1196+
reveal_type(z2["val"]) # N: Revealed type is "builtins.int"
1197+
1198+
z3: C[Unpack[Tuple[int, ...]], bool]
1199+
reveal_type(z3) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.bool})"
1200+
reveal_type(z3["fn"]) # N: Revealed type is "def (*builtins.int)"
1201+
reveal_type(z3["val"]) # N: Revealed type is "builtins.bool"
1202+
1203+
t: B[int, Unpack[Tuple[int, str]]]
1204+
reveal_type(t) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.int, builtins.str), 'val': builtins.bool})"
1205+
1206+
t2: C[int, Unpack[Tuple[int, str]]]
1207+
reveal_type(t2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.int), 'val': builtins.str})"
1208+
1209+
def test(x: int, y: str) -> None: ...
1210+
td = B({"fn": test, "val": False})
1211+
reveal_type(td) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1212+
td2 = C({"fn": test, "val": False})
1213+
reveal_type(td2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1214+
1215+
def bad() -> int: ...
1216+
td3 = B({"fn": bad, "val": False}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]")
1217+
td4 = C({"fn": bad, "val": False}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]")
1218+
1219+
[builtins fixtures/dict.pyi]
1220+
[typing fixtures/typing-typeddict.pyi]
1221+
1222+
[case testVariadicTypedDictDeriveStar]
1223+
from typing import Tuple, Callable, Generic, TypedDict, TypeVar
1224+
from typing_extensions import TypeVarTuple
1225+
1226+
T = TypeVar("T")
1227+
Ts = TypeVarTuple("Ts")
1228+
class A(TypedDict, Generic[*Ts]):
1229+
fn: Callable[[*Ts], None]
1230+
1231+
class B(A[*Ts]):
1232+
val: bool
1233+
1234+
class C(A[*Ts], Generic[*Ts, T]):
1235+
val: T
1236+
1237+
1238+
y: B[int, str]
1239+
reveal_type(y) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1240+
reveal_type(y["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1241+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1242+
1243+
y2: C[int, str, bool]
1244+
reveal_type(y2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1245+
reveal_type(y2["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1246+
reveal_type(y2["val"]) # N: Revealed type is "builtins.bool"
1247+
1248+
z: B[*Tuple[int, ...]]
1249+
reveal_type(z) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (*builtins.int), 'val': builtins.bool})"
1250+
reveal_type(z["fn"]) # N: Revealed type is "def (*builtins.int)"
1251+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1252+
1253+
z2: C[*Tuple[int, ...]]
1254+
reveal_type(z2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.int})"
1255+
reveal_type(z2["fn"]) # N: Revealed type is "def (*builtins.int)"
1256+
reveal_type(z2["val"]) # N: Revealed type is "builtins.int"
1257+
1258+
z3: C[*Tuple[int, ...], bool]
1259+
reveal_type(z3) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.bool})"
1260+
reveal_type(z3["fn"]) # N: Revealed type is "def (*builtins.int)"
1261+
reveal_type(z3["val"]) # N: Revealed type is "builtins.bool"
1262+
1263+
t: B[int, *Tuple[int, str]]
1264+
reveal_type(t) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.int, builtins.str), 'val': builtins.bool})"
1265+
1266+
t2: C[int, *Tuple[int, str]]
1267+
reveal_type(t2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.int), 'val': builtins.str})"
1268+
1269+
[builtins fixtures/dict.pyi]
1270+
[typing fixtures/typing-typeddict.pyi]
1271+
11621272
[case testFixedUnpackWithRegularInstance]
11631273
from typing import Tuple, Generic, TypeVar
11641274
from typing_extensions import Unpack

0 commit comments

Comments
 (0)