-
Notifications
You must be signed in to change notification settings - Fork 140
Open
astral-sh/ruff
#21739Labels
genericsBugs or features relating to ty's generics implementationBugs or features relating to ty's generics implementation
Milestone
Description
Summary
Summary
Incomplete type narrowing with TypeIs on generic types after early return
Description
Type narrowing fails for generic types when using a TypeIs type guard with an early return pattern. Pyright correctly narrows the type in this scenario, but ty does not. The issue does not occur with simple (non-generic) classes.
Minimal Reproducible Example
# exp.py
from typing import Union, Generic, TypeVar
from typing_extensions import TypeIs
T = TypeVar('T', covariant=True)
E = TypeVar('E', covariant=True)
class Ok(Generic[T]):
def __init__(self, value: T):
self._value = value
@property
def ok_value(self) -> T:
return self._value
def is_err(self) -> bool:
return False
class Err(Generic[E]):
def __init__(self, error: E):
self._error = error
@property
def err_value(self) -> E:
return self._error
def is_err(self) -> bool:
return True
Result = Union[Ok[T], Err[E]]
def is_err(result: Result[T, E]) -> TypeIs[Err[E]]:
"""Type guard to check if a result is an Err"""
return result.is_err()
# ------------------------------------------------------------
def some_function() -> Result[int, Exception]:
condition = False
if condition is False:
return Err(Exception("Test error"))
return Ok(1)
def use_some_function():
result = some_function()
if is_err(result):
print(result.err_value)
return None
# After the is_err() check and early return,
# result should be narrowed to Ok[int]
okay = result.ok_value # <- ty reports warning here
print(okay)Expected Behavior
After the if is_err(result): return None check, result should be narrowed to Ok[int], making result.ok_value valid without warnings.
Actual Behavior
uv run ty check exp.py
warning[possibly-missing-attribute]: Attribute `ok_value` may be missing on object of type `(Ok[int] & ~Err[Unknown]) | (Err[Exception] & ~Err[Unknown])`
--> exp.py:57:12
|
55 | # After the is_err() check and early return,
56 | # result should be narrowed to Ok[int]
57 | okay = result.ok_value # <- ty reports warning here
| ^^^^^^^^^^^^^^^
58 | print(okay)
|
info: rule `possibly-missing-attribute` is enabled by defaultComparison with Pyright
Pyright correctly handles this pattern with 0 errors, 0 warnings for both generic and non-generic types.
- Simple classes with
TypeIs: Works correctly in bothtyand Pyright - Generic classes with
TypeIs: Fails inty, works in Pyright
Notes
- Pattern matching (
match/case) works correctly in both type checkers, even with generics - The issue specifically occurs when combining:
- Generic types (e.g.,
Union[Ok[T], Err[E]]) TypeIstype guards- Early return control flow
- Generic types (e.g.,
- Type narrowing appears to be incomplete when the type guard eliminates one branch of a generic union
Environment
tyversion: 0.0.1a29- Python version: 3.13
Version
0.0.1a29
Metadata
Metadata
Assignees
Labels
genericsBugs or features relating to ty's generics implementationBugs or features relating to ty's generics implementation