Repository navigation
Introduce a type for an unknown invariant generic param that behaves strictly #2169
Description
Activity
- addedtopic: featureDiscussions about new features for Python's type annotationsDiscussions about new features for Python's type annotations
on Feb 6, 2026 What behavior would you want from a third method:
def bing(self) -> T: ...
if this should be callable and return
object(that is, could be any Python object), then I think what you are looking for is what in the ty type-checker we call the "top materialization" ofFoo[Any]-- that is, the infinite union of all possible specializations ofFoo. In ty (withfrom ty_extensions import Top) you can spell this type asTop[Foo[Any]]. It has the behavior thatTin all covariant positions becomesobject, and in all contravariant positions becomesNever-- when you take out aT, it could be any Python object, and you aren't allowed to put in aT, because you don't know what type is allowed.Note that if
Foowere covariant inT(implies noTin contravariant position), thenTop[Foo[Any]]is equivalent toFoo[object]. And ifFoowere contravariant inT, it is equivalent toFoo[Never]. So you only really need a dedicated representation for this in this case of invariant generics.I don't think we should use the name
Unknownto represent this concept, because several existing Python type checkers already have a type namedUnknownwhich is something different: it is equivalent toAny, but originates from a missing annotation rather than from an explicitAnyannotation.In a sense the type you are proposing is dual to
Any-- it is "an unknown static type", but with the strictest possible behavior (nothing can be assigned to it, it can only be assigned toobject) rather than the most forgiving possible behavior (as withAny).I am curious what your concrete use case for this is. In ty we use this type internally to represent the effect of
if isinstance(x, Foo)as an intersection -- but usually you want it to simplify out of the resulting intersection, because having an instance ofTop[Foo[Any]]is typically not very useful.My concrete usecase is when I have code that dynamically chooses different types based on data, e.g. if I am deserializing an object.
def make_foo(data: dict) -> Foo[Unknown]: if data['type'] == 'str': return Foo[str](...) if data['type'] == 'int': return Foo[int](...)I like
Foo[Unknown]more thanTop[Foo[Any]]becauseFoo[Any]itself isn't quite correct, but I agree that another name would be better to avoid clashing with existing type checkers.Please discuss any further ideas in the original issue. Closing this as a duplicate.
Or maybe even better: start a new discussion with a more specific idea. People seem to have pretty different visions of what an Unknown type should do and what it's good for.
Reacted by Carl Meyer and ShantanuYes -- I don't think the idea proposed here is actually the same as any of the prior proposals in #1835.
- changed the title
[-]Introduce an Unknown type[/-][+]Introduce a type for an unknown invariant generic param that behaves strictly[/+]on Mar 18, 2026
This is the same issue as #1835, which was closed -- the given reason was that
objectcan be used instead ofUnknown. However, this is not the case. Consider:I want a type that represents "Foo with unknown T", which I can call baz on but not bar.
This doesn't work if I use object: