[ty] Fix disjointness of type guards and boolean literals (#28363)

This commit is contained in:
Alex Waygood authored and GitHub committed 2026-09-08 11:00:10 +01:00
1 parent 12e65d6d7e
commit 321eebc2d6
3 files changed
+52

No files matched your search

@@ -159,6 +159,27 @@ def f(x: N, y: int, z: O):
reveal_type(x is not z) # revealed: Literal[True]
```
## Identity comparisons with type guard results
`TypeIs` and `TypeGuard` functions return booleans, so comparing their results with `True` or
`False` can succeed or fail.
```py
from typing_extensions import TypeGuard, TypeIs
def is_int(x: object) -> TypeIs[int]:
return isinstance(x, int)
def is_int_guard(x: object) -> TypeGuard[int]:
return isinstance(x, int)
def f(x: object):
reveal_type(is_int(x) is True) # revealed: bool
reveal_type(is_int(x) is False) # revealed: bool
reveal_type(is_int_guard(x) is True) # revealed: bool
reveal_type(is_int_guard(x) is False) # revealed: bool
```
## Identity comparisons see through type aliases
```toml
@@ -754,6 +754,32 @@ static_assert(is_disjoint_from(str, TypeGuard[str]))
static_assert(is_disjoint_from(str, TypeIs[str]))
```
Either kind of type guard can return `True` or `False`, so both overlap with each boolean literal.
```py
from typing import Literal
static_assert(not is_disjoint_from(TypeGuard[str], Literal[True]))
static_assert(not is_disjoint_from(TypeGuard[str], Literal[False]))
static_assert(not is_disjoint_from(TypeIs[str], Literal[True]))
static_assert(not is_disjoint_from(TypeIs[str], Literal[False]))
static_assert(not is_disjoint_from(Literal[True], TypeGuard[str]))
static_assert(not is_disjoint_from(Literal[False], TypeGuard[str]))
static_assert(not is_disjoint_from(Literal[True], TypeIs[str]))
static_assert(not is_disjoint_from(Literal[False], TypeIs[str]))
```
The integer literals `0` and `1` are distinct from boolean literals, so they remain disjoint from
type guard return types.
```py
static_assert(is_disjoint_from(TypeGuard[str], Literal[0]))
static_assert(is_disjoint_from(TypeIs[str], Literal[1]))
static_assert(is_disjoint_from(Literal[1], TypeGuard[str]))
static_assert(is_disjoint_from(Literal[0], TypeIs[str]))
```
### `Protocol`
A protocol is disjoint from another type if any of the protocol's members are available as an
@@ -3767,6 +3767,11 @@ impl<'a, 'c, 'db> DisjointnessChecker<'a, 'c, 'db> {
})
}
(Type::TypeIs(_) | Type::TypeGuard(_), Type::LiteralValue(literal))
| (Type::LiteralValue(literal), Type::TypeIs(_) | Type::TypeGuard(_)) => {
ConstraintSet::from_bool(self.constraints, !literal.is_bool())
}
(Type::TypeIs(_) | Type::TypeGuard(_), Type::NominalInstance(instance))
| (Type::NominalInstance(instance), Type::TypeIs(_) | Type::TypeGuard(_)) => {
// A boolean literal must be an instance of exactly `bool`