Type Hinting Error with Union Types and Type Variables in Python 3.11

446 views Asked by At

I've encountered a type hinting issue in Python 3.11 using Pylance in Visual Studio Code, and I'm looking for insights into why this error is occurring and how to resolve it. Here's my code:

from typing import TypeAlias, TypeVar

Data: TypeAlias = int | str
DataVar = TypeVar("DataVar", int, str)

class A:
    def __init__(self):
        pass

    def do_something(self, X: Data) -> Data:
        return self._foo(X)  # <---- Pylance raises an error here!!

    def _foo(self, X: DataVar) -> DataVar:
        return X

However, Pylance raises the following error:

Argument of type "Data" cannot be assigned to parameter "X" of type "DataVar@_foo" in function "_foo"
  Type "Data" is incompatible with constrained type variable "DataVar"

I'm struggling to understand why this error is occurring. As far as I can tell, Data is a flexible type that can be either an int or a str, and _foo should be able to accept it as an argument. If I had provided the types in the reverse order, i.e. do_something expects a DataVar and _foo gets Data, I would expect an error (which is indeed raised)

  1. Why is Pylance raising this error?
  2. Is there a correct way to annotate the types to avoid this error?
  3. Is this a limitation or false positive with the type checker in Python 3.11?

Any insights or suggestions on how to address this issue would be greatly appreciated.

1

There are 1 answers

2
S.B On BEST ANSWER

What is the type of Data?

from typing import reveal_type

Data: TypeAlias = int | str
reveal_type(Data) # Runtime type is 'UnionType'

It's a Union Type.

On the other hand the TypeVar documentation says:

Using a constrained type variable(like what you defined), however, means that the TypeVar can only ever be solved as being exactly one of the constraints given.

So X can only be either int or str(or their subclasses). Union type is not a subclass of any of the constraints.

This is how the typing system works. Mypy also complains:

error: Value of type variable "DataVar" of "_foo" of "A" cannot be "int | str"  [type-var]

A simple fix to this is to bound DataVar to Data:

DataVar = TypeVar("DataVar", bound=Data)