Skip to content

Type alias expansion sometimes interferes with type variable binding #3924

Description

@ilevkivskyi

Consider the following two examples, I think they should be equivalent, since logically expansion of type aliases should happen before any other steps. (At least this would be consistent and natural for those familiar with C macros):

from typing import TypeVar, Callable
T = TypeVar('T')

def test1() -> Callable[[T], T]: ...
reveal_type(test1) # Revealed type is 'def () -> def [T] (T`-1) -> T`-1'
reveal_type(test1()) # Revealed type is 'def [T] (T`-1) -> T`-1'

F = Callable[[T], T]
def test2() -> F[T]: ...
reveal_type(test2) # Revealed type is 'def [T] () -> def (T`-1) -> T`-1'
reveal_type(test2()) # Revealed type is 'def (<nothing>) -> <nothing>'

Here test1 works as expected, while if I use an alias something strange happens with test2. Namely notice a subtle difference between their types.

def () -> def [T] (T`-1) -> T`-1
and
def [T] () -> def (T`-1) -> T`-1

I have noticed few similar scenarios, they all seem to be related to Callable.

Activity

  1. gvanrossum commented on Sep 6, 2017

    @gvanrossum
    Member

    Sounds like the code that made -> Callable[[T], T] possible in the first place still has some bugs...

  2. mvolfik commented on Feb 3, 2021

    @mvolfik

    I have an issue which is, I believe, related to this:

    from typing import TypeVar, Callable
    
    T = TypeVar("T")
    Function = Callable[[T], T]
    
    a: Function         = ...
    b: Function[T]      = ...
    c: Callable[[T], T] = ...

    (save as test.pyi, or replace the ellipsis with lambda x: x, function created in function etc.)

    Mypy always disallows a, with --strict disallows b and always allows c. Tested with 0.800 and 0.810+dev.9e0e23e97653dab3a558d34340486e7a66f7d6f0.

    How much is this related, should I file a separate issue? I would expect either a or b (no idea which one though) to be entirely correct

    When I was looking for duplicates, #8922 also looked related

  3. added a commit that references this issue on Jul 6, 2021
  4. AlexWaygood commented on Apr 4, 2022

    @AlexWaygood
    Member

    I just closed #8273 as a duplicate of this issue, but there was some interesting discussion about possible causes and solutions in that issue.

  5. added a commit that references this issue on May 18, 2023
  6. sirosen commented on Aug 21, 2023

    @sirosen

    Coming at this from #13449, where there's some discussion of my case (slightly different from the OP there), I have a question:

    Is there a benefit or meaning to allowing <nothing> as a type inference? I'm wondering what the potential benefit and harm would be of making --strict enable rejection of any inference which produces <nothing> as an error.

    The only instances of <nothing> which I've seen are avoidable errors related to scoping of type variables. Are there use-cases in which it is the right type to infer and matches the user's intent?

  7. ilevkivskyi commented on Aug 21, 2023

    @ilevkivskyi
    MemberAuthor

    <nothing> is a subtype of all types, a.k.a. bottom type, a.k.a. error type, a.k.a. item type of empty collection. It behaves as it is for various reasons (both good and bad). What you are asking about is not related to this issue. If you can clearly describe what exactly do you want (with examples), you can open a new issue.

  8. added a commit that references this issue on Oct 23, 2024
  9. antoniogamizdelgado commented on Nov 5, 2024

    @antoniogamizdelgado

    Any update on this? This is interfering with django ninja api decorator type hints (getting the following error: error: Untyped decorator makes function) when defining endpoints as:

    router:: Router = Router()
    
    
    @router.get(
        "endpoint",
        response={
            200: SomeModel,
            400: Error,
            401: Error,
            404: Error,
            500: Error,
        },
    )
    def some_endpoint(request: HttpRequest -> SomeModel:
        pass
  10. added a commit that references this issue on Dec 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions