Python Type Narrowing with TypeGuard: Narrowing Return type that is a TypeVar

148 views Asked by At

Consider the following case:

from typing import Union, TypeVar, List
from typing_extensions import TypeGuard

_T = TypeVar('_T', str, int)

def is_str(val: _T) -> TypeGuard[str]:
    return isinstance(val, str)

def is_int(val: _T) -> TypeGuard[int]:
    return isinstance(val, int)

def process_str_int(data: _T) -> _T:
    if is_str(data):
        # At this point, `data` is narrowed down to `list[str]`
        print("Returning a string")
        return data
    elif is_int(data):
        print("Returning an int")
        return data

def process_str_int_with_isinstance(data: _T) -> _T:
    if isinstance(data, str):
        # At this point, `data` is narrowed down to `list[str]`
        print("Returning a string")
        return data
    elif isinstance(data, int):
        print("Returning an int")
        return data

process_str_int("hello")

At the point of return in process_str_int I get an error in pyright complaining that 'Expression of type "str" cannot be assigned to return type "_T@process_list"' (and similarly for the int case). The python interpreter is python 3.9

This does not happen with isinstance, where the TypeVar is correctly narrowed down and thus the correct return type is inferred and matched against the return value

How can I acheive a similar behaviour using custom type guards?

0

There are 0 answers