# Permitting more precise inferred types from ambiguous overloads

**URL:** <https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776>\
**Category:** Typing\
**Created:** [August 27, 2026, 8:00pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776 "2026-08-27T20:00:27Z")\
**Posts on this page:** 20\
**Page:** 1

<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:** [August 27, 2026, 8:00pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/1 "2026-08-27T20:00:27Z")

</div>

The typing spec and conformance suite currently require that if overload resolution is ambiguous due to gradual argument types, the return type must be inferred as `Any`.

This is motivated by maintaining graduality of inputs in the output. If an argument type of `Any` makes an overloaded call possibly resolve to either `int` or `str`, the return type should be usable as either `int` or `str` without error, otherwise we’ve effectively lost the graduality requested by the original `Any`. This motivation is well-founded.

But often a more precise gradual type can meet this requirement, and preserve more unambiguous type information. Mypy already implements this in some cases (contrary to the current spec). If an overloaded call might ambiguously return `list[int]` or `list[str]`, mypy will infer `list[Any]` rather than just `Any`. This preserves the unambiguous “is a list” information, while still allowing the return to be used either as a `list[str]` or a `list[int]`.

I propose a change to the typing spec and conformance suite that replaces the mandate to infer `Any` with a mandate to infer some type that meets the gradual requirement outlined above. `Any` obviously meets this requirement, so inferring `Any` will continue to be valid under my proposed change. But this change will make mypy’s current behavior spec-conforming, and will allow type checkers to experiment with inferring more precise types without losing conformance.

The PR is at [Allow non-Any gradual return types for ambiguous overloads by carljm · Pull Request #2350 · python/typing · GitHub](https://github.com/python/typing/pull/2350) – comments welcome.

---

<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:** [August 27, 2026, 8:20pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/2 "2026-08-27T20:20:08Z")

</div>

While this seems like a positive change, I want to make sure I understand the outcome of a specific case.

Given the below:

```py
@overload
def example(foo: A) -> int: ...
@overload
def example(foo: B) -> str: ...
def example(foo): ...

def something_else() -> A | B: ...

example(something_else()) # I would expect `int | str`, not Any and not a future AnyOf[int, str] either

```

In this case, it isn’t overloads that are ambiguous, it’s the input could be either and isn’t gradual, so the return type should reflect that. If I’m reading the proposal correctly, this is still the case and I would be on board with the proposal.

---

<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:** [August 27, 2026, 8:24pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/3 "2026-08-27T20:24:02Z")

</div>

Yeah, your example is unaffected by this proposal, because this step in the overload evaluation algorithm applies only when multiple overloads match due to a gradually-typed argument.

Your example exercises a different step in overload evaluation (union expansion) – when multiple overloads match due to union expansion, their return types should correctly be unioned together.

---

<div class="post-metadata">

**Author:** ![ImogenBits](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/imogenbits/32/29566_2.png) [@ImogenBits](https://discuss.python.org/u/ImogenBits)\
**Post date:** [August 28, 2026, 6:04am UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/4 "2026-08-28T06:04:43Z")

</div>

Am I understanding it correctly that we ideally want the return type to be exactly the gradual type that contains all of the remaining return types? And we don’t want to require this behaviour because type checkers generally don’t implement arbitrary gradual types?

---

<div class="post-metadata">

**Author:** ![randolf-scholz](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/randolf-scholz/32/11123_2.png) [@randolf-scholz](https://discuss.python.org/u/randolf-scholz)\
**Post date:** [August 28, 2026, 6:56am UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/5 "2026-08-28T06:56:23Z")

</div>

Yes, ideally would probably be `AnyOf[R1, R2, ..., Rn]` where `R1`, …, `Rn` are the matching return types. `AnyOf` is a gradual version of `Union` that is assignable to _any of_ its constituents.

- [AnyOf - Union for return types · Issue #566 · python/typing · GitHub](https://github.com/python/typing/issues/566)
- [Allow returning `AnyOf` rather than `Any` for amiguous overloads. · Issue #2196 · python/typing · GitHub](https://github.com/python/typing/issues/2196)

---

<div class="post-metadata">

**Author:** ![tmk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tmk/32/10935_2.png) [@tmk](https://discuss.python.org/u/tmk)\
**Post date:** [August 28, 2026, 12:39pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/6 "2026-08-28T12:39:05Z")

</div>

The newly added text in the linked PR seems to exactly describe the `AnyOf` type linked above:

> For example, if the remaining return types are `int` and `str`, the result  
> should be assignable to both `int` and `str`, and both `result + 1` and  
> `result.upper()` should be accepted. Inferring the ordinary union `int | str`  
> would not satisfy these requirements.

I guess one worry with this is that you’re instructing type checkers to return a type that can’t currently be expressed in the typing system.

---

<div class="post-metadata">

**Author:** ![duncathan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/duncathan/32/25521_2.png) [@duncathan](https://discuss.python.org/u/duncathan)\
**Post date:** [August 28, 2026, 1:10pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/7 "2026-08-28T13:10:40Z")

</div>

Well no. `Any` would remain a perfectly valid inference here. The change only allows type checkers to get more specific if they like.

---

<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:** [August 28, 2026, 1:15pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/8 "2026-08-28T13:15:37Z")

</div>

Yes, you all have it right. The ideal type would be the gradual type that can materialize to precisely the possible return types of the ambiguous overloads, but I don’t think we are in a position to require that type without a PEP that allows expressing such types.

This is intended to be an incremental change that allows type checkers to improve on the status quo in whatever ways work within their model (as mypy already does), without blocking on a PEP-level addition to the type system. It’s not meant to preclude (nor require) possible future updates to specify something more prescriptive.

---

<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:** [August 28, 2026, 1:23pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/9 "2026-08-28T13:23:12Z")

</div>

The wording in this change is satisfied by the precise `AnyOf` type but does not require it. It’s also satisfied by simply `Any`. Or by some types more precise than `Any` but less precise than exactly the `AnyOf`; for example mypy’s `list[Any]` is not precisely `AnyOf[list[int], list[str]]`, but it’s a much closer approximation than `Any`.

---

<div class="post-metadata">

**Author:** ![tmk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tmk/32/10935_2.png) [@tmk](https://discuss.python.org/u/tmk)\
**Post date:** [August 28, 2026, 2:03pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/10 "2026-08-28T14:03:49Z")

</div>

> [@carljm](#):
>
> It’s also satisfied by simply `Any`. Or by some types more precise than `Any` but less precise than exactly the `AnyOf`; for example mypy’s `list[Any]` is not precisely `AnyOf[list[int], list[str]]`, but it’s a much closer approximation than `Any`.

Makes sense 👍

---

<div class="post-metadata">

**Author:** ![rchen152](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rchen152/32/16955_2.png) [@rchen152](https://discuss.python.org/u/rchen152)\
**Post date:** [August 29, 2026, 9:56am UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/11 "2026-08-29T09:56:47Z")

</div>

While this proposal permits more precise inferred return types than the current spec, mypy, pyright, and pyrefly all deviate from the overload evaluation section of the spec, and at least for pyrefly, this would force us to produce _less_ precise types across the board.

I ran pyrefly on mypy primer with its default semantics (choose a return type that all materializations of all other candidate return types are assignable to, or `Any` if such a return type doesn’t exist), then with those semantics plus requiring that the chosen return type be assignable to all other candidate return types (let’s call this “safe” semantics). There were changes in 122 projects, and the error delta was:

```python
Error kind Added Removed Net
--------------------------- ------- ------- -------
unknown-argument-type 6507 239 +6268
unknown-variable-type 3365 83 +3282
implicit-bool 92 352 -260
assert-type 300 19 +281
no-any-return-implicit 260 0 +260
unsupported-operation 74 184 -110
bad-argument-type 39 200 -161
missing-attribute 22 216 -194
[remaining error kinds had less than 100 added or removed errors apiece]
--------------------------- ------- ------- -------
Total 10842 1679 +9163

```

The dominant impact is added errors like `unknown-argument-type` from producing more `Any`s, although there are also some removed errors, like `unsupported-operation`, which could be fixed false positives. (Note: the error kinds for unknown types are off-by-default, so I’m not claiming that pyrefly would actually produce 11k more errors, just using the errors as a measure of how types would shift.)

I also took a closer look at two specific projects, numpy and scipy-stubs, which both have a lot of overloaded functions and extensive typing test suites, and found plenty of examples where an `assert_type` expected a type that would violate the proposed new requirement.

(Everything from this point onward is an analysis that I was originally compiling for [Spec change: ambiguous arguments in overload call evaluation](https://discuss.python.org/t/spec-change-ambiguous-arguments-in-overload-call-evaluation/106891) . I kept putting off posting it because I didn’t have a good solution to propose. These numbers are about 2 months old.)

I ran pyrefly on numpy and scipy-stubs’ typing tests with default semantics, then safe semantics, and examined new `assert_type` failures. There were 169 new failures, and the categories they fell into were:

- In 81 cases (48%), one of the overloads returned a union containing the other return types. E.g., `A | B`, `A`, `B` =\> `A | B` is selected.

- In 56 cases (33%), one of the overloads returned a union with `Any`, which caused the other return types to be assignable to it. E.g. `A | Any`, `B` =\> `A | Any` is selected.

- 29 cases (17%) were the union case but with the union in a type argument. E.g., `X[A | B]`, `X[A]`, `X[B]` =\> `X[A | B]` is selected.

- Finally, in 3 cases (2%), one of the overloads returned a variable-length tuple and the others fixed-length tuples. E.g., `tuple[A, ...]`, `tuple[A, A]`, `tuple[A, A, A]` =\> `tuple[A, ...]` is selected.

For what it’s worth, mypy and pyright seem to get the expected answer in these cases not by picking intelligently but by selecting the first matching overload [1], and the overloads are ordered so the most general one comes first. Regardless, the pattern seems to be that rather than all of the overload signatures being equal, so to speak, one of them is intended to be a fallback. Requiring that the inferred return type be assignable to all of the return types would prevent a type checker from choosing the fallback in the above cases. The mypy primer numbers suggest that this isn’t an isolated issue.

[1] Example of pyright picking the first overload in an ambiguous case:

```python
from typing import Any, overload

class A[T]: ...

@overload
def f(x: A[int]) -> int: ...
@overload
def f(x: A[str]) -> str: ...
def f(x) -> Any: ...

def g(x: A[Any]):
    reveal_type(f(x)) # int

```

Example with mypy (note that the type alias seems to be needed to hide the `Any` from mypy):

```python
from typing import Any

class A[T1, T2]: ...

type X[T] = A[Any, T]

from typing import overload
@overload
def f(x: A[int, int]) -> int: ...
@overload
def f(x: A[str, int]) -> str: ...
def f(x) -> int | str:
    return 0
    
def g(x: X[int], y: A[Any, int]) -> None:
    reveal_type(f(x)) # int
    reveal_type(f(y)) # but this is `Any`!

```

---

<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:** [August 29, 2026, 10:38am UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/12 "2026-08-29T10:38:04Z")

</div>

In case of NumPy the `assert_type` tests are almost always specifically tuned so that mypy accept them. For scipy-stubs they are checked using mypy, (based)pyright, and pyrefly, where I often chose the path of least resistance and tuned the `assert_type` tests for the majority; i.e. minimizing the type ignore comments. So for practicality I followed current checker behavior rather than the typing spec.

So don’t worry too much about those; I wouldn’t mind adjusting those type tests to make them more compatible with the spec 🙂

---

<div class="post-metadata">

**Author:** ![duncathan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/duncathan/32/25521_2.png) [@duncathan](https://discuss.python.org/u/duncathan)\
**Post date:** [August 29, 2026, 3:18pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/13 "2026-08-29T15:18:29Z")

</div>

> [@rchen152](#):
>
> at least for pyrefly, this would force us to produce _less_ precise types across the board

Could you provide an example of such an affected function? I also feel it’s a bit unfair to say that this spec change would _force_ less precise types when you’re already deviating from the spec anyway 😛

---

<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:** [August 29, 2026, 5:27pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/14 "2026-08-29T17:27:53Z")

</div>

The change I’m proposing is, I think, a clear improvement over the current language in the spec and the requirements in the conformance suite today. Today the spec language absolutely requires `Any` in all cases of gradual ambiguity; this proposal permits more precise gradual types than `Any`, without losing the input graduality.

Your suggestion is that maybe we should go even _further_, and be also willing to accept some false positives due to inferring a static return type with greater confidence than can be substantiated by the gradual arguments to the call.

That’s a reasonable discussion to have, but I think it gets much more complex and debatable, and I would strongly prefer to separate that question from my current proposal, so that we avoid “letting the (maybe) better be the enemy of the good”, and end up with no improvement at all in the short term.

So I would prefer to limit this discussion to this question: given that accepting this proposal does not close the door to further future proposals to adjust the specification of ambiguous overloads, does the current proposal improve on the status quo?

---

<div class="post-metadata">

**Author:** ![rchen152](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rchen152/32/16955_2.png) [@rchen152](https://discuss.python.org/u/rchen152)\
**Post date:** [August 29, 2026, 5:29pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/15 "2026-08-29T17:29:32Z")

</div>

Fair, if I’m being more careful with my wording, _following_ this requirement would force pyrefly to produce less precise types.

To be clear, if this change lands, I intend to update pyrefly to respect it. For some background, pyrefly followed the overload evaluation section of the spec exactly until this April, when I changed its default behavior because it disagreed with the existing ecosystem in too many places. I also put up a proposed spec change at the time, because the intent was never to deviate from the spec long-term; IMO that’s bad for the type system in all sorts of ways.

I also really don’t want to churn pyrefly’s behavior repeatedly, so I’d like to make sure that we’re really okay with the cost of type checkers following the spec here. If we’re all on the same page, I’m not going to try to block this or anything; this would actually make things easier on me from a maintenance perspective because I could just delete pyrefly’s entire `--spec-compliant-overloads` flag and replace it with a three-line loop 🙂

[Here](https://github.com/scipy/scipy-stubs/blob/b7103b7e15d3e59e9c6e60cf9a79a667bbc43b37/tests/stats/test_ttest.pyi#L109) is an actual example from scipy-stubs, although I’m not sure how illuminating it is.

---

<div class="post-metadata">

**Author:** ![Jelle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jelle/32/1049_2.png) [@Jelle](https://discuss.python.org/u/Jelle)\
**Post date:** [August 30, 2026, 2:18pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/16 "2026-08-30T14:18:31Z")

</div>

I support this change but I’d like to discuss the background a bit more.

Let’s look at this example a bit more (taken from the conformance suite):

```python
@overload
def example5(obj: list[int]) -> list[int]: ...
@overload
def example5(obj: list[str]) -> list[str]: ...
def example5(obj: Any) -> list[Any]:
    return []

def check_example5(b: list[Any]) -> None:
    assert_type(example5(b), Any)

```

If we pass a fully static type to `example5`, we always get a fully static result: `list[int]` (for lists of ints), `list[str]` (for lists of strs), or an error (for anything else). But what if we get a gradual type such as `list[Any]` that could materialize to the parameter type of either overload?

In that case, the return type could be that of any of the overloads. And we ideally want to preserve the gradual guarantee, which means that if we replace the `list[Any]` type with a more static one, we should not _remove_ any errors that the type checker emitted with the gradual type.

So we want the inferred return type to be (1) a gradual type that can materialize to all of the return types from the overloads. Ideally it (2) shouldn’t be able to materialize to too much else, because that makes the code too permissive. In a type system with intersections, one way to do that is to infer the type `(A & B) | ((A | B) & Any)`, which is a type that can materialize to the union of the two types, their intersection, and anything in between. In our example, this would simplify to `(list[int] | list[str]) & Any`.

But we currently don’t have intersections in the specified type system. The current spec instead picks Any, which fulfills the first requirement (it can materialize to any return type) but doesn’t do great on the second (it can materialize to a great many unrelated types too). Carl’s proposal is essentially to specify only this first requirement and allow type checkers to decide how precise to make their gradual types. Any would still fullfil this requirement, but so would `list[Any]` or `(list[int] | list[str]) & Any`.

The pyrefly behavior described by Rebecca doesn’t follow the gradual guarantee. Its behavior is visible in this modification of the example:

```python
from typing import Any, overload, assert_type

@overload
def example5(obj: list[int]) -> list[int] | list[str]: ...
@overload
def example5(obj: list[str]) -> list[str]: ...
def example5(obj: Any) -> list[Any]:
    return []

def check_example5(b: list[Any]) -> None:
    reveal_type(example5(b))

```

Here pyrefly infers `list[int] | list[str]`. This violates the gradual guarantee, because if `b` was a `list[str]` instead, we’d get the more precise type `list[str]`. I’d prefer to maintain the guarantee here and infer a type like `list[str] | (list[int] & Any)`, or `list[Any]`.

---

<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:** [August 31, 2026, 4:52pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/17 "2026-08-31T16:52:05Z")

</div>

Thanks @Jelle for outlining this in more detail.

I do think we are in a slightly difficult position vis-a-vis the current ecosystem, because some prominent projects that use overloads heavily have (reasonably) designed their overloads around the current behavior of mypy and pyright. (In an ironic/unfortunate twist, it looks like mypy intended to implement gradual handling of gradually ambiguous overloads, but has a bug where type aliases – which should be transparent – are allowed to “hide” the gradual ambiguity, and that mypy bug may have been the original trigger for much of this pick-the-first-overload behavior.)

For type checkers (like pyright and pyrefly, or mypy when type aliases are involved) which currently pick one of the gradually-ambiguous overloads rather than constructing a gradual type that encompasses all of them, I expect that a switch to simply inferring `Any` instead will, in the short term, be experienced by users as a regression in precision, to the extent that current ecosystem projects have molded their overloads around the current behavior.

One example of this “molding around the current behavior” is that pandas-stubs will often add an initial overload with a signature like `(Sequence[Never]) -> Series[Any]`, just to “catch” `Sequence[Any]`, specifically so that type-checkers which “pick the first” won’t wrongly/arbitrarily pick the next overload (e.g. `(Sequence[bool]) -> Series[bool]`) when the argument is `Sequence[Any]`. But this is not a very clear pattern (nobody actually expects an argument of type `Sequence[Never]`), and it causes problems when type checkers are able to correctly recognize that e.g. the empty string inhabits `Sequence[Never]`. So this is a case where the current ecosystem has already had to develop sub-optimal workarounds for the “guess at one of the overloads” behavior.

I think the precision regression experienced by users will be much less to the extent that type checkers are able to infer more precise types (like mypy’s `list[Any]` in my OP example, or the even more precise types outlined by @Jelle) that still follow the gradual requirement. In the pandas case I discuss above, that could allow type checkers to automatically infer `Series[Any]` (or a more precise type that still gradually covers all the possibilities) rather than just `Any` for a gradually ambiguous case, which would remove the need for that `Sequence[Never]` overload entirely.

So although I don’t expect the transition to be entirely painless, I do feel like the best path forward is to deprecate this “pick one” behavior and preserve graduality when overloads are gradually ambiguous.

---

<div class="post-metadata">

**Author:** ![rchen152](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rchen152/32/16955_2.png) [@rchen152](https://discuss.python.org/u/rchen152)\
**Post date:** [August 31, 2026, 6:44pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/18 "2026-08-31T18:44:45Z")

</div>

For Pyrefly, I’ve [updated](https://github.com/facebook/pyrefly/commit/0d8e9f6319c3d54a0d4005fa602d8a69d2e3c2f8) how it resolves ambiguous overloads, so that the “most general” return type it selects also has to be assignable to all of the candidate return types. Thanks all for the discussion.

---

<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:** [September 1, 2026, 10:03pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/19 "2026-09-01T22:03:44Z")

</div>

I think that “must be assignable to all other candidate overload return types” may not be a sufficient guard to implement the graduality requirement that the spec requires (either now, or after my change).

Consider this case (simplified from the scipy case you linked above):

```py
from typing import Any, assert_type, overload

@overload
def f(x: list[int]) -> float | Any: ...
@overload
def f(x: object) -> Any: ...

x: list[Any]
reveal_type(f(x))

```

In this case the argument type `list[Any]` gives us no basis for preferring one overload over the other; `list[Any]` may be e.g. `list[str]`, which would match the second overload, not the first.

Currently pyright and pyrefly infer `float | Any` for this call, meaning the return type would error if assigned to e.g. a `str`. So `float | Any` and `Any` are mutually assignable to each other, but that does not mean that `float | Any` can be used in all the same ways as `Any`. So per my PR, a type checker would not be free to pick arbitrarily between `float | Any` and `Any` here – the only valid inference for this call would be `Any`.

---

<div class="post-metadata">

**Author:** ![rchen152](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rchen152/32/16955_2.png) [@rchen152](https://discuss.python.org/u/rchen152)\
**Post date:** [September 1, 2026, 10:30pm UTC](https://discuss.python.org/t/permitting-more-precise-inferred-types-from-ambiguous-overloads/108776/20 "2026-09-01T22:30:31Z")

</div>

Ah, that’s my bad, I didn’t implement the “permits operations supported by any of them without  
errors” piece. Thanks for the correction.
