basedpyright icon indicating copy to clipboard operation
basedpyright copied to clipboard

`Unpack` should work on generics that are bound to a `TypedDict`

Open KotlinIsland opened this issue 1 year ago • 6 comments

from typing import TypedDict, Unpack


class TDict(TypedDict):
    pass


def f[T: TDict](**kwargs: Unpack[T]) -> T: ...


reveal_type(f(a=1))
reveal_type(f(b="a"))

because TypedDict is structural, this should allow all TypedDicts to work

KotlinIsland avatar Aug 27 '24 00:08 KotlinIsland

doesn't necessarily need to be a TypedDict, i don't see why it shouldn't work if oit's bound to Mapping[str, object]:

from typing import Unpack
from collections.abc import Mapping

class Foo[T: Mapping[str, object]]:
    def f(self, **kwargs: Unpack[T]) -> T: ...

_ = Foo[Mapping[str, object]]().f(a='asdf')

DetachHead avatar Aug 27 '24 05:08 DetachHead

doesn't necessarily need to be a TypedDict, i don't see why it shouldn't work if oit's bound to Mapping[str, object]:

from typing import Unpack
from collections.abc import Mapping

class Foo[T: Mapping[str, object]]:
    def f(self, **kwargs: Unpack[T]) -> T: ...

_ = Foo[Mapping[str, object]]().f(a='asdf')
class AmongusMapping(Mapping[str, str]):...
Foo[AmongusMapping]().f(a="asdf")

KotlinIsland avatar Aug 27 '24 07:08 KotlinIsland

it should still be able to unpack them even if it's a different subtype of mapping imo

DetachHead avatar Aug 27 '24 08:08 DetachHead

pyright (and hence basedpyright) already unofficially (and temporarily) supports Unpacking tuple-bound TypeVars (see https://github.com/microsoft/pyright/discussions/10012):

from typing import Any, Unpack


def concat[S: tuple[Any, ...], T: tuple[Any, ...]](_x: S, _y: T, /) -> tuple[Unpack[S], Unpack[T]]: ...

a: tuple[int] = (42,)
b: tuple[str] = ("hi",)
z: tuple[()] = ()
reveal_type(concat(a, a))	# tuple[int, int]
reveal_type(concat(a, b))	# tuple[int, str]
reveal_type(concat(a, z))	# tuple[int]

(playground - basedpyright even propagates literal types down the line)

This works for variadic positional arguments as well:

from typing import Any, Unpack


def pack[T: tuple[Any, ...]](*args: Unpack[T]) -> tuple[Unpack[T]]:
    return args

reveal_type(pack("3", 14))  # tuple[Literal['3'], Literal[14]]

(playground)

IMHO, this (Unpack[T] where T is a TypeVar with an Unpack-able bound) would be a very useful type system feature to have and support officially.

Edit: I'm using the latest basedpyright version available on the playground as of now, i.e., 1.32.0

RBerga06 avatar Oct 23 '25 10:10 RBerga06

let us know if that functionality ever gets removed and we'll add it back :)

DetachHead avatar Oct 23 '25 10:10 DetachHead

Upstream issue for the original proposal (Unpack[TD] where TD: TypedDict): https://github.com/python/typing/issues/1399

RBerga06 avatar Nov 26 '25 16:11 RBerga06

Upstream issue for the original proposal (Unpack[TD] where TD: TypedDict): python/typing#1399

mine was earlier https://github.com/python/typing/issues/1395

KotlinIsland avatar Dec 08 '25 11:12 KotlinIsland