1

I'm trying to introduce Generic typing to a function of mine, but am getting a pylance error of:

Expression of type "A" cannot be assigned to declared type "T@func"
  Type "A" cannot be assigned to type "T@func"

I've reduce my problem in my code to this simplified version:

from typing import TypeVar, Union, List


class A:
    def __init__(self, arg: str) -> None:
        self.arg = arg


class B:
    def __init__(self, arg: int) -> None:
        self.arg = arg


T = TypeVar("T", A, B)


def getA() -> A:
    return A("a")


def getB() -> B:
    return B(1)


def func(arg: T) -> T:
    out: T
    if isinstance(arg, A):
        out = getA()
    elif isinstance(arg, B):
        out = getB()

    return out


print(func(A("a")))

The error occurs at both out = getA() and out = getB()

is pyright not able to accurately infer types here? Am I making a mistake?

dvreed77
  • 2,217
  • 2
  • 27
  • 42
  • I tried this in PyCharm and all looks good. If I change T's definition to `T = TypeVar("T", A, str)`, then `out=getB()` line gives a warning - so it works correctly. I suppose it's an issue of your type checker. – michcio1234 Mar 29 '22 at 13:53
  • i'm wondering when generics is needed in python, since python is dynamic typing? – Lei Yang Mar 29 '22 at 13:59
  • @LeiYang You can add static type analysis to python using PyRight. Definitely helpful when working in a larger team and you need more concrete contracts between functions. – dvreed77 Mar 29 '22 at 14:42
  • @michcio1234 Do you know if you are using pyright? When I manually run this file through pyright I get the same error, so its not my vscode pylance plugin – dvreed77 Mar 29 '22 at 14:50

1 Answers1

0

When you bind T to func via arg, you are saying T is the type of arg in func. When you declare out as a T, you are saying out will always be the same type as arg. So by assinging the result of getA to out, you are assigning something you know is an A to a T, but out must be a B if arg is a B.

To avoid the type errors, you can just write func like this:

def func(arg: T) -> T:
    if isinstance(arg, A):
        return getA()
    if isinstance(arg, B):
        return getB()

Within your conditions, you actually have narrowed the type of arg to A and B, so if you return inside the conditions, you know you are returning a T, i.e. the same type as arg.

ringo
  • 978
  • 7
  • 16
  • The warning remains even if I remove `B` from the code. It seems as if pyright does not infer that `A` is a valid generic `T` even though `T` is bound to `A`. – msrc Mar 01 '23 at 11:46