@@ -1203,6 +1203,22 @@ def check_func_def(
12031203 # visible from *inside* of this function/method.
12041204 ref_type : Type | None = self .scope .active_self_type ()
12051205
1206+ if typ .type_is :
1207+ arg_index = 0
1208+ # For methods and classmethods, we want the second parameter
1209+ if ref_type is not None and (not defn .is_static or defn .name == "__new__" ):
1210+ arg_index = 1
1211+ if arg_index < len (typ .arg_types ) and not is_subtype (
1212+ typ .type_is , typ .arg_types [arg_index ]
1213+ ):
1214+ self .fail (
1215+ message_registry .NARROWED_TYPE_NOT_SUBTYPE .format (
1216+ format_type (typ .type_is , self .options ),
1217+ format_type (typ .arg_types [arg_index ], self .options ),
1218+ ),
1219+ item ,
1220+ )
1221+
12061222 # Store argument types.
12071223 for i in range (len (typ .arg_types )):
12081224 arg_type = typ .arg_types [i ]
@@ -2178,6 +2194,8 @@ def check_override(
21782194 elif isinstance (original , CallableType ) and isinstance (override , CallableType ):
21792195 if original .type_guard is not None and override .type_guard is None :
21802196 fail = True
2197+ if original .type_is is not None and override .type_is is None :
2198+ fail = True
21812199
21822200 if is_private (name ):
21832201 fail = False
@@ -5643,7 +5661,7 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap:
56435661 def find_isinstance_check (self , node : Expression ) -> tuple [TypeMap , TypeMap ]:
56445662 """Find any isinstance checks (within a chain of ands). Includes
56455663 implicit and explicit checks for None and calls to callable.
5646- Also includes TypeGuard functions.
5664+ Also includes TypeGuard and TypeIs functions.
56475665
56485666 Return value is a map of variables to their types if the condition
56495667 is true and a map of variables to their types if the condition is false.
@@ -5695,7 +5713,7 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM
56955713 if literal (expr ) == LITERAL_TYPE and attr and len (attr ) == 1 :
56965714 return self .hasattr_type_maps (expr , self .lookup_type (expr ), attr [0 ])
56975715 elif isinstance (node .callee , RefExpr ):
5698- if node .callee .type_guard is not None :
5716+ if node .callee .type_guard is not None or node . callee . type_is is not None :
56995717 # TODO: Follow *args, **kwargs
57005718 if node .arg_kinds [0 ] != nodes .ARG_POS :
57015719 # the first argument might be used as a kwarg
@@ -5721,15 +5739,31 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM
57215739 # we want the idx-th variable to be narrowed
57225740 expr = collapse_walrus (node .args [idx ])
57235741 else :
5724- self .fail (message_registry .TYPE_GUARD_POS_ARG_REQUIRED , node )
5742+ kind = (
5743+ "guard" if node .callee .type_guard is not None else "narrower"
5744+ )
5745+ self .fail (
5746+ message_registry .TYPE_GUARD_POS_ARG_REQUIRED .format (kind ), node
5747+ )
57255748 return {}, {}
57265749 if literal (expr ) == LITERAL_TYPE :
57275750 # Note: we wrap the target type, so that we can special case later.
57285751 # Namely, for isinstance() we use a normal meet, while TypeGuard is
57295752 # considered "always right" (i.e. even if the types are not overlapping).
57305753 # Also note that a care must be taken to unwrap this back at read places
57315754 # where we use this to narrow down declared type.
5732- return {expr : TypeGuardedType (node .callee .type_guard )}, {}
5755+ if node .callee .type_guard is not None :
5756+ return {expr : TypeGuardedType (node .callee .type_guard )}, {}
5757+ else :
5758+ assert node .callee .type_is is not None
5759+ return conditional_types_to_typemaps (
5760+ expr ,
5761+ * self .conditional_types_with_intersection (
5762+ self .lookup_type (expr ),
5763+ [TypeRange (node .callee .type_is , is_upper_bound = False )],
5764+ expr ,
5765+ ),
5766+ )
57335767 elif isinstance (node , ComparisonExpr ):
57345768 # Step 1: Obtain the types of each operand and whether or not we can
57355769 # narrow their types. (For example, we shouldn't try narrowing the
0 commit comments