Open
Labels
topic: featureDiscussions about new features for Python's type annotationsDiscussions about new features for Python's type annotations
Description
from typing import _T as T, TypeVar
L = TypeVar("L", bound=list[T]) # error: Type variable "typing._T" is unbound
def foo(l: L) -> L | T: # error: A function returning TypeVar should receive at least one argument containing the same Typevar
res = l[0]
if res:
return res
return l
a: list[int] | list[str]
reveal_type(foo) # "def [L <: list[T?], T] (l: L) -> L | T"
reveal_type(foo(a)) # list[int] | list[str]Expected:
reveal_type(foo) # "def [T, L <: list[T]] (l: L) -> L | T"
reveal_type(foo(a)) # list[int] | list[str] | int | strMypy should understand that T should be bound to foo from L
This is identical to generic TypeAliases:
L: TypeAlias = list[T]
a: L[int]Here T is unbound, yet it's a valid and semantically sound expression.
Typescript example
The same idea could be represented in TypeScript as:
declare function foo<T, L extends T[]>(l: L): L | T
declare let a: number[] | string[]
let b = foo(a)Although TS fails to infer the correct type here. (it infers as unknown)
basedmypy
This is partially supported in basedmypy
Reacted by DetachHead, Shahriar Heidrich, aiudirog, Ethan Resnick, Noam Nol, Chris Bouchard, Jonathan Coates, Audiopolis, Valentin Stauber, Ashrith Sagar and 2 more
Metadata
Metadata
Assignees
Labels
topic: featureDiscussions about new features for Python's type annotationsDiscussions about new features for Python's type annotations
Activity
erictraut commentedon Jul 22, 2022
This concept doesn't make sense. A TypeVar used within the bound of a TypeVar would have no defined scope. It should therefore be considered an error to use a TypeVar within a bound or constraint expression. PEP 484 isn't as clear as it could be on this point. I've attempted to remedy that omission in draft PEP 695.
DetachHead commentedon Jul 22, 2022
how so? typescript supports it:
KotlinIsland commentedon Jul 22, 2022
@erictraut In my opinion this is identical to generic
TypeAliases:Here
Tis unbound, yet it's a valid and semantically sound expression.The second example is how I understand the example in the OP.
AlexWaygood commentedon Jul 22, 2022
Things like this should be discussed in the python/typing issue tracker or on the typing-sig mailing list, not the mypy issue tracker. We'd need agreement on the spec before individual type checkers could start supporting this.
KotlinIsland commentedon Jul 23, 2022
@AlexWaygood Could you transfer the issue to python/typing?
smheidrich commentedon Dec 20, 2023
As this is basically a subset of the functionality offered by higher-kinded types/TypeVars (HKTs) requested in #548, I'm wondering if it might make sense to have this as a first step before full HKT support?
The example from the beginning
would, in the suggested HKT syntax, be written as
I think it might be confusing if both of these possibilities coexisted, so maybe the syntax for this feature could adopt the one from HKTs but put restrictions on it to limit it to non-full-HKT cases, i.e. making it forbidden to use
Lparametrized with anything other thanT? But then again, if the entire PEP for this were to only make sense in anticipation of the subsequent HKT PEP, it might be better to skip it and go with full HKTs immediately? 🤔RomanValov commentedon Jan 16, 2026
Here is my use case for "generic" bounds. Though in contrast to other examples in this and relevant issues (especially #548 ), my case would benefit from bound being simply another type variable rather then generic construct.
I would like to implement decorator-based generic handler registry in my code. But crucial point for the registry is the possibility to apply decorator to functions with inexact (but compatible) call signatures. And preserve the signatures once being applied. It's where the construct with bound comes into play with bound being exact handler signature:
Compare
Registry0,Registry1andRegistry2in the snippet:Registry0is non-generic implementation which perfectly handles (and preserves) non-exact function signatures of example handlers.Registry1is an attempt to implement generic registry but it's too rigid and narrows function signatures after being applied. Hence handlers no longer being able to call each other as shown in the example.Registry2would potentially resolve limitations of Registry1, but it requires type variable (of the method) to be bound with another type variable (of the class).[-](🎁) Support `TypeVar`s in the bounds of other `TypeVar`s[/-][+](🎁) Support type variables in the bounds of other type variables[/+]