# Constrained type vars with \`Any\` default

**URL:** <https://discuss.python.org/t/constrained-type-vars-with-any-default/104799>\
**Category:** Typing\
**Tags:** typing\
**Created:** [November 10, 2025, 5:05pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799 "2025-11-10T17:05:45Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![srittau](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/srittau/32/237_2.png) [@srittau](https://discuss.python.org/u/srittau)\
**Post date:** [November 10, 2025, 5:05pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/1 "2025-11-10T17:05:45Z")

</div>

I originally brought this up in [Constrained TypeVars with Any default · Issue #20184 · python/mypy · GitHub](https://github.com/python/mypy/issues/20184) , but @sterliakov pointed out that mypy’s behavior is correct according to the typing spec:

* * *

In [python/typeshed#13683](https://github.com/python/typeshed/pull/13683) I’m trying to change a type var to the following:

```python
_I = TypeVar("_I", int, bytes, float, bool, default=Any) # possibly memoryview types

```

Unfortunately, mypy complains: `error: TypeVar default must be one of the constraint types [misc]`. While this is of course generally sound advice, a default of `Any` should always be permissible, as this allows `memoryview` to stand for any possible memoryview type. Without it, users are forced to write `memoryview[Any]` in every instance, which is unreasonable.

* * *

This is what the [spec currently says](https://typing.python.org/en/latest/spec/generics.html#constraints):

> For constrained `TypeVar` s, the default needs to be one of the constraints. A type checker should generate an error even if it is a subtype of one of the constraints.

I suggest to change the typing spec to allow `Any` as default for constrained type vars, for the reasons given above.

---

<div class="post-metadata">

**Author:** ![jorenham](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jorenham/32/27806_2.png) [@jorenham](https://discuss.python.org/u/jorenham)\
**Post date:** [November 10, 2025, 5:34pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/2 "2025-11-10T17:34:10Z")

</div>

> [@srittau](#):
>
> `_I = TypeVar("_I", int, bytes, float, bool, default=Any)`

Wouldn’t it work like this?

```py
_I = TypeVar("_I", int, bytes, float, bool, Any, default=Any)

```

edit: ah no nevermind; that would permit everything

* * *

Or otherwise:

```py
_I = TypeVar("_I", bound=float | bytes, default=Any)

```

---

<div class="post-metadata">

**Author:** ![JoBe](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jobe/32/26961_2.png) [@JoBe](https://discuss.python.org/u/JoBe)\
**Post date:** [November 10, 2025, 5:38pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/3 "2025-11-10T17:38:56Z")

</div>

The second looks like it should work, I’ve used that before.

---

<div class="post-metadata">

**Author:** ![srittau](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/srittau/32/237_2.png) [@srittau](https://discuss.python.org/u/srittau)\
**Post date:** [November 10, 2025, 8:09pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/4 "2025-11-10T20:09:59Z")

</div>

Please note that bounds are not equivalent to constraints, as the former also allow subtypes. (Although I seem to remember some discussion whether constraints are really necessary and couldn’t be replaced with bounds.)

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [November 10, 2025, 9:07pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/5 "2025-11-10T21:07:04Z")

</div>

Any, with it’s current definition, as a default breaks the purpose of constraints.

To update the spec here such that users don’t need to write `memoryview[Any]`, you would need a new symbol meaning “ ~~Any~~ One of the valid constraints”.

Some prior stuff I’ve brought up about fixing the meaning of Any, as it currently causes problems by allowing more than only things that are otherwise valid within gradual typing, would fix this and allow Any to be a reasonable default itself, but there was little appetite among typechecker authors last time.

You’ll also run into some typecheckers erroring for implicit Any, so users are going to have to write that anyway unless you can convince typecheckers to stop doing that (also harmful to gradual typing)

---

<div class="post-metadata">

**Author:** ![carljm](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/carljm/32/959_2.png) [@carljm](https://discuss.python.org/u/carljm)\
**Post date:** [November 10, 2025, 11:57pm UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/6 "2025-11-10T23:57:27Z")

</div>

> [@mikeshardmind](#):
>
> You’ll also run into some typecheckers erroring for implicit Any

My assumption is that silencing these errors must be the underlying motivation for explicitly specifying `default=Any` on a typevar?

Otherwise it seems redundant, since `Any` is already specified as the “default default” for a typevar.

> [@mikeshardmind](#):
>
> Any, with it’s current definition, as a default breaks the purpose of constraints.
> 
> To update the spec here such that users don’t need to write `memoryview[Any]`, you would need a new symbol meaning “ ~~Any~~ One of the valid constraints”.

I don’t agree with this. The meaning of `Any` is “an unknown static type.” This is compatible with “…but only some of the possible materializations would be valid in this position.” We already support, for example, specializing a `TypeVar` to `Any` (and/or giving it a default of `Any`), even if it has an upper bound, meaning that there would be some materializations of that `Any` that would not be valid specializations for that typevar.

I don’t see why `Any` should be invalid as a default specialization of any typevar for which it is a valid explicit specialization. So if it is allowed to say `C[Any]` explicitly, where `C` is generic over a constrained typevar, I think it should also be valid to give `Any` as the default for that constrained typevar.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [November 11, 2025, 12:15am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/7 "2025-11-11T00:15:30Z")

</div>

> [@carljm](#):
>
> I don’t agree with this. The meaning of `Any` is “an unknown static type.” This is compatible with “…but only some of the possible materializations would be valid in this position.” We already support, for example, specializing a `TypeVar` to `Any` (and/or giving it a default of `Any`), even if it has an upper bound, meaning that there would be some materializations of that `Any` that would not be valid specializations for that typevar.

I don’t find that that definition actually avoids the issue with constraints (specifically, the full definition found in the specification), but even if we bend a few things to say that it also strongly requires the things this would imply to be necessary, without directly spelling out their requirement, to work everywhere within the type system, then, type checkers today aren’t actually following the behavior outlined there.

It’s not possible for a typechecker to adhere to this and allow the use of Any without tracking bounds separately on every instance of Any in types, something that I don’t believe typecheckers currently do, as that was a blocker on actually requiring support for negation of gradual types. If I’m wrong about this and the situation has changed, please do correct me as that would simplify efforts elsewhere.

---

<div class="post-metadata">

**Author:** ![carljm](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/carljm/32/959_2.png) [@carljm](https://discuss.python.org/u/carljm)\
**Post date:** [November 11, 2025, 12:18am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/8 "2025-11-11T00:18:53Z")

</div>

> [@mikeshardmind](#):
>
> It’s not possible for a typechecker to adhere to this and allow the use of Any without tracking bounds separately on every instance of Any in types

Can you clarify why this prevents a default-specialization of `Any` for a constrained typevar, if it does not prevent an explicit specialization of `Any` for that same constrained typevar (which is already allowed)?

And if it’s a problem for a constrained typevar, can you clarify why it’s not a problem for a bound typevar (for which it is also true that only a subset of possible types would be a valid specialization)?

> [@mikeshardmind](#):
>
> I don’t find that that definition actually avoids the issue with constraints (specifically, the full definition found in the specification), but even if we bend a few things to say that it also strongly requires the things this would imply to be necessary, without directly spelling out their requirement, to work everywhere within the type system, then, type checkers today aren’t actually following the behavior outlined there.

I’m not able to extract any concrete meaning from anything in this paragraph. Could you be more specific in your referents?

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [November 11, 2025, 12:32am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/9 "2025-11-11T00:32:52Z")

</div>

> [@carljm](#):
>
> Can you clarify why this prevents a default-specialization of `Any` for a constrained typevar, if it does not prevent an explicit specialization of `Any` for that same constrained typevar (which is already allowed)?

The explicit use of Any there runs into the same problems as the default and both should be allowed eventually and shouldn’t be allowed until the issues with not tracking Any are fixed.

Ideally, that would be by removing this line in the specification:

> In Python’s type system, we don’t take the gradual guarantee as a strict requirement, but it’s a useful guideline.

and then fixing all of the places that violate the gradual guarantee.

> [@carljm](#):
>
> I’m not able to extract any concrete meaning from anything in this paragraph. Could you be more specific in your referents?

I don’t have my notes with me on this as I’m currently traveling. I remember that it had something to do with the pairing of mutual exclusivity in constraints, with the places the type system is currently lossy due to not tracking certain things. It essentially leads to situations where the mutual exclusivity is lost.

Sorry if that’s unsatisfying, but reworking through the places that the definitions being inconsistent leads to unsoundness is not something I want to be spending my time on right now

I also remember that more consistent definitons without exceptions in them would remedy this. It’s possible I’ve missed an improvement that enables it, or missed that an improvement in definitions I was aware of was enough to enable it, but I have a nagging feeling this is not the case.

---

<div class="post-metadata">

**Author:** ![carljm](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/carljm/32/959_2.png) [@carljm](https://discuss.python.org/u/carljm)\
**Post date:** [November 11, 2025, 1:20am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/10 "2025-11-11T01:20:02Z")

</div>

> [@mikeshardmind](#):
>
> The explicit use of Any there runs into the same problems as the default and both should be allowed eventually and shouldn’t be allowed until the issues with not tracking Any are fixed.

I strongly suspect that disallowing explicit specialization of a constrained TypeVar to `Any` is a non-starter, given ecosystem impact. Specializing TypeVars to `Any` is quite common. So if there are issues with that, those are pre-existing (and long-standing) issues. Regardless of what we do in this thread, we have to live with those issues or find solutions to them.

Given that explicit specialization to `Any` is already allowed, I don’t see how allowing the default specialization to be `Any` introduces _new_ issues, so I don’t see any reason here to oppose the proposed change. It’s inconsistent to allow an explicit specialization and not allow the same specialization as a default.

---

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [November 11, 2025, 2:05am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/11 "2025-11-11T02:05:39Z")

</div>

> [@carljm](#):
>
> I strongly suspect that disallowing explicit specialization of a constrained TypeVar to `Any` is a non-starter, given ecosystem impact. Specializing TypeVars to `Any` is quite common.

While this is common, constrained type variables, rather than simply bound ones, are significantly less common.

I worry that it seems we’re frequently collecting more and more cases where certain things don’t fit together well within the type system, and rather than fix or disallow the things that aren’t working well until they can be fixed, we just say that the ecosystem impact would prevent that.

> [@carljm](#):
>
> Given that explicit specialization to `Any` is already allowed, I don’t see how allowing the default specialization to be `Any` introduces _new_ issues, so I don’t see any reason here to oppose the proposed change. It’s inconsistent to allow an explicit specialization and not allow the same specialization as a default.

It may create new wider impact for existing typesystem issues, as this is only coming up in trying to change the typeshed from a place not currently using a constrained typevariable, to one that does, in a type that will have a lot of impact (memoryview)

---

<div class="post-metadata">

**Author:** ![jorenham](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jorenham/32/27806_2.png) [@jorenham](https://discuss.python.org/u/jorenham)\
**Post date:** [November 11, 2025, 7:32am UTC](https://discuss.python.org/t/constrained-type-vars-with-any-default/104799/12 "2025-11-11T07:32:14Z")

</div>

BTW, this is yet another use-case for `AnyOf` ([AnyOf - Union for return types · Issue #566 · python/typing · GitHub](https://github.com/python/typing/issues/566)), i.e.

```py
type _NotABouncyCastle = AnyOf[bool, int, float, bytes]
_I = TypeVar(
    "_I",
    bool, int, float, bytes, _NotABouncyCastle,
    default=_NotABouncyCastle,
)

```

I suppose it’d also be a better “default default” than the current `Any`.
