TypeIs is designed to use isinstance (inheritance) semantics for type narrowing. Callable is a structural type, and the spec is not clear what the behavior should be in this case.
As a rule of thumb, a type checker should use the same type narrowing logic ā and get results that are consistent with ā its handling of isinstance().
However, I would say TypeIs should work equally fine with all types, structural or nominal. I propose that this requirement be added to the spec.
The less rule of thumb bit right before the part of the specification you quoted is:
Formally, type NP should be narrowed to Aā§R, the intersection of A and R , and type NN should be narrowed to Aā§Ā¬R, the intersection of A and the complement of R . In practice, the theoretic types for strict type guards cannot be expressed precisely in the Python type system. Type checkers should fall back on practical approximations of these types.
I think including TypeIs prior to Intersections was a mistake and said as much at the time, but you may need to wait for Intersection work to be finished to be able to expect consistent behavior across typecheckers here given that the formal definition relies on them, but then kicks that down the curb and says āuse an approximationā
Using the current draft of intersections, this would work as you seem to expect.
TypeIs may not be what you want here. Using TypeGuard should work for the positive branch today.
Thereās a problem with using intersections to narrow here because there is a type that is āmore compatibleā than your intent. Intersecting a gradual callable with a structural type that accepts no parameters in that you havenāt ruled out that it can also take parameters.
A function like:
def x(foo: int = 0, /) -> None: ...
Is compatible with the structural type Callable[[], None] and Callable[..., Any]
The tough questions around Any have answers already, so most of what is left is writing up examples, specifically showing what this solves, how it interacts with other āspecialā parts of the type system such as with dataclass_transform, and some language changes around TypeIs and Never.
As TypeIs is basically a limited Intersection already and specified as such, any improvement in the specification of the expectations of intersections, including adding them directly, will improve usability of TypeIs.