Skip to content

Incorrect "redundant cast" for linked TypeVar T and type[T] variables #17045

@richardxia

Description

@richardxia

Bug Report

Given a TypeVar T with multiple, concrete constraints, if you write a generic function that takes an argument of type T, another argument of type type[T], and return a value of type T, mypy does not seem to have consistent behavior when it comes to handling the return type of individual expressions. Attempting to return a value without casting hits an Incompatible return value type error, while attempting to cast causes a Redundant cast error.

To Reproduce

from decimal import Decimal
from typing import TypeVar, cast

T = TypeVar("T", Decimal, float)


def foo(x: T, x_type: type[T]) -> T:
    if x_type == float:
        return cast("T", 1.0)
    if x_type == Decimal:
        return cast("T", Decimal("2.0"))
    raise ValueError("unreachable")


def foo2(x: T, x_type: type[T]) -> T:
    if x_type == float:
        return 1.0
    if x_type == Decimal:
        return Decimal("2.0")
    raise ValueError("unreachable")

https://mypy-play.net/?mypy=1.9.0&python=3.12&flags=strict&gist=4a0bc37034dcbcd5a18fabebeb5cbff5

Expected Behavior

I expected either one of foo() or foo2() to be accepted by mypy. I don't necessarily expect mypy to be smart enough to infer the the linkage between narrowing the type of x_type and relating it to the type of x, but I at least expect to be able to explicitly cast the return expression without hitting a Redundant cast error.

Actual Behavior

main.py:9: error: Redundant cast to "float"  [redundant-cast]
main.py:11: error: Redundant cast to "Decimal"  [redundant-cast]
main.py:17: error: Incompatible return value type (got "float", expected "Decimal")  [return-value]
main.py:19: error: Incompatible return value type (got "Decimal", expected "float")  [return-value]
Found 4 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.7.1 (but the mypy Playground I linked to above was 1.9.0)
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
    warn_unused_ignores = true
    warn_redundant_casts = true
    warn_unused_configs = true
    warn_unreachable = true
    warn_return_any = true
    strict = true
    disallow_untyped_decorators = true
    disallow_any_generics = false
    implicit_reexport = false
    show_error_codes = true
  • Python version used: 3.11.6 (mypy Playground was 3.12)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions