Skip to content

Introduce a type for an unknown invariant generic param that behaves strictly #2169

Description

@vladfi1

This is the same issue as #1835, which was closed -- the given reason was that object can be used instead of Unknown. However, this is not the case. Consider:

class Foo(tp.Generic[T]):
  def bar(self, x: T) -> T:
    ...

  def baz(...):
    ...

I want a type that represents "Foo with unknown T", which I can call baz on but not bar.

foo: Foo[Unknown] = ...
foo.bar(1)  # type error, int cannot be assigned to Unknown
foo.baz()  # works fine because no T is needed

This doesn't work if I use object:

foo: Foo[object] = ...
foo.bar(1)  # typechecks because int is a subtype of object

Activity

  1. added
    topic: featureDiscussions about new features for Python's type annotations
    on Feb 6, 2026
  2. carljm commented on Feb 6, 2026

    @carljm
    Member

    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" of Foo[Any] -- that is, the infinite union of all possible specializations of Foo. In ty (with from ty_extensions import Top) you can spell this type as Top[Foo[Any]]. It has the behavior that T in all covariant positions becomes object, and in all contravariant positions becomes Never -- when you take out a T, it could be any Python object, and you aren't allowed to put in a T, because you don't know what type is allowed.

    Note that if Foo were covariant in T (implies no T in contravariant position), then Top[Foo[Any]] is equivalent to Foo[object]. And if Foo were contravariant in T, it is equivalent to Foo[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 Unknown to represent this concept, because several existing Python type checkers already have a type named Unknown which is something different: it is equivalent to Any, but originates from a missing annotation rather than from an explicit Any annotation.

    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 to object) rather than the most forgiving possible behavior (as with Any).

    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 of Top[Foo[Any]] is typically not very useful.

  3. vladfi1 commented on Feb 7, 2026

    @vladfi1
    Author

    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 than Top[Foo[Any]] because Foo[Any] itself isn't quite correct, but I agree that another name would be better to avoid clashing with existing type checkers.

  4. srittau commented on Feb 9, 2026

    @srittau
    Collaborator

    Please discuss any further ideas in the original issue. Closing this as a duplicate.

  5. JelleZijlstra commented on Feb 9, 2026

    @JelleZijlstra
    Member

    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.

  6. carljm commented on Feb 9, 2026

    @carljm
    Member

    Yes -- I don't think the idea proposed here is actually the same as any of the prior proposals in #1835.

  7. changed the title [-]Introduce an Unknown type[/-] [+]Introduce a type for an unknown invariant generic param that behaves strictly[/+] on Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: featureDiscussions about new features for Python's type annotations

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions