You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec/conformance: function with TypeVar default and default argument used in context. #2213
Since the default argument value is assignable to the default type var type, def get is well-defined
arg.get() implicitly uses None of type None as the argument. Therefore, we have an implicit constraint S <: None when .get is called without explicit default.
Therefore, result: str = arg.get() should raise an assignment type error (str | None not assignalbe to str).
Currently, only pyright succeeds on this test, and type checker results diverge.
pyright: no errors (true negative on def get, true positive on result) (pyright playground)
mypy: false positive on def get, false negative on result.
ty: false positive on def get, true positive on result. (playground)
pyrefly: false positive on def get, false negative on result (playground)
zuban: false postive on def get, false negative on result.
I'd write the test case with assert_type(arg.get(), str | None).
Well, actually all type checkers succeed for this case, the problem is that with the outer context str, checkers like mypy forget about the S <: None constraints when no explicit default is given, and happily present S=str as a solution.
Without outer context, the default S=None is used instead.
So the spec I am essentially arguing for here for is:
If a function parameter is annotated with a type-variable with a default, then the default argument for that parameter must be assignable to that type var default.
defget[T=object](default: T=object()) ->T: ... # OK, object() is assignable to objectdefget[T=object](default: T="123") ->T: ... # Also OK, "123" is assignable to objectdefget[T=str](default: T=None) ->T: ... # E: None is not assignable to str
If for a parameter annotated with a TypeVar with default, no arguments are provided in a function call, then this should be treated as-if an argument of the default type were provided
defget[T=None](default: T=None) ->T: ...
get() # identical inferenence as get(None)defget[T=object](default: T=None) ->T: ...
get() # identical inference as get( object() ), although real default is None
In particular in my original example, both result: str = arg.get() and result: str = arg.get(default=None) should give identical inference.
Reacted by Jelle Zijlstra, Neil Girdhar, Michael H, Thibaut Decombe and Zverev Konstantin
Activity
JelleZijlstra commentedon Mar 8, 2026
The semantics of TypeVar defaults on generic functions are explicitly unspecified (https://typing.python.org/en/latest/spec/generics.html#function-defaults), so adding this case would require a spec change. Your use case seems reasonable though!
I'd write the test case with
assert_type(arg.get(), str | None).randolf-scholz commentedon Mar 8, 2026
@JelleZijlstra
Well, actually all type checkers succeed for this case, the problem is that with the outer context
str, checkers likemypyforget about theS <: Noneconstraints when no explicit default is given, and happily presentS=stras a solution.Without outer context, the default
S=Noneis used instead.randolf-scholz commentedon Mar 8, 2026
So the spec I am essentially arguing for here for is:
If a function parameter is annotated with a type-variable with a default, then the default argument for that parameter must be assignable to that type var default.
If for a parameter annotated with a
TypeVarwith default, no arguments are provided in a function call, then this should be treated as-if an argument of the default type were providedIn particular in my original example, both
result: str = arg.get()andresult: str = arg.get(default=None)should give identical inference.