[ty] Add partial support for TypeIs (#18589)
## Summary Part of [#117](https://github.com/astral-sh/ty/issues/117). `TypeIs[]` is a special form that allows users to define their own narrowing functions. Despite the syntax, `TypeIs` is not a generic and, on its own, it is meaningless as a type. [Officially](https://typing.python.org/en/latest/spec/narrowing.html#typeis), a function annotated as returning a `TypeIs[T]` is a <i>type narrowing function</i>, where `T` is called the <i>`TypeIs` return type</i>. A `TypeIs[T]` may or may not be bound to a symbol. Only bound types have narrowing effect: ```python def f(v: object = object()) -> TypeIs[int]: ... a: str = returns_str() if reveal_type(f()): # Unbound: TypeIs[int] reveal_type(a) # str if reveal_type(f(a)): # Bound: TypeIs[a, int] reveal_type(a) # str & int ``` Delayed usages of a bound type has no effect, however: ```python b = f(a) if b: reveal_type(a) # str ``` A `TypeIs[T]` type: * Is fully static when `T` is fully static. * Is a singleton/single-valued when it is bound. * Has exactly two runtime inhabitants when it is unbound: `True` and `False`. In other words, an unbound type have ambiguous truthiness. It is possible to infer more precise truthiness for bound types; however, that is not part of this change. `TypeIs[T]` is a subtype of or otherwise assignable to `bool`. `TypeIs` is invariant with respect to the `TypeIs` return type: `TypeIs[int]` is neither a subtype nor a supertype of `TypeIs[bool]`. When ty sees a function marked as returning `TypeIs[T]`, its `return`s will be checked against `bool` instead. ty will also report such functions if they don't accept a positional argument. Addtionally, a type narrowing function call with no positional arguments (e.g., `f()` in the example above) will be considered invalid. ## Test Plan Markdown tests. --------- Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
@@ -19,7 +19,6 @@ def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
|
||||
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
|
||||
def g() -> TypeGuard[int]: ...
|
||||
def h() -> TypeIs[int]: ...
|
||||
def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.kwargs) -> R_co:
|
||||
reveal_type(args) # revealed: tuple[@Todo(Support for `typing.ParamSpec`), ...]
|
||||
reveal_type(kwargs) # revealed: dict[str, @Todo(Support for `typing.ParamSpec`)]
|
||||
|
||||
Reference in New Issue
Block a user