Special typing construct for marking user-defined type predicate functions. ``TypeIs`` can be used to annotate the return type of a user-defined type predicate function. ``TypeIs`` only accepts a single type argument. At runtime, functions marked this way should return a boolean and ac
(self, parameters)
| 930 | |
| 931 | @_SpecialForm |
| 932 | def TypeIs(self, parameters): |
| 933 | """Special typing construct for marking user-defined type predicate functions. |
| 934 | |
| 935 | ``TypeIs`` can be used to annotate the return type of a user-defined |
| 936 | type predicate function. ``TypeIs`` only accepts a single type argument. |
| 937 | At runtime, functions marked this way should return a boolean and accept |
| 938 | at least one argument. |
| 939 | |
| 940 | ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static |
| 941 | type checkers to determine a more precise type of an expression within a |
| 942 | program's code flow. Usually type narrowing is done by analyzing |
| 943 | conditional code flow and applying the narrowing to a block of code. The |
| 944 | conditional expression here is sometimes referred to as a "type predicate". |
| 945 | |
| 946 | Sometimes it would be convenient to use a user-defined boolean function |
| 947 | as a type predicate. Such a function should use ``TypeIs[...]`` or |
| 948 | ``TypeGuard[...]`` as its return type to alert static type checkers to |
| 949 | this intention. ``TypeIs`` usually has more intuitive behavior than |
| 950 | ``TypeGuard``, but it cannot be used when the input and output types |
| 951 | are incompatible (e.g., ``list[object]`` to ``list[int]``) or when the |
| 952 | function does not return ``True`` for all instances of the narrowed type. |
| 953 | |
| 954 | Using ``-> TypeIs[NarrowedType]`` tells the static type checker that for |
| 955 | a given function: |
| 956 | |
| 957 | 1. The return value is a boolean. |
| 958 | 2. If the return value is ``True``, the type of its argument |
| 959 | is the intersection of the argument's original type and |
| 960 | ``NarrowedType``. |
| 961 | 3. If the return value is ``False``, the type of its argument |
| 962 | is narrowed to exclude ``NarrowedType``. |
| 963 | |
| 964 | For example:: |
| 965 | |
| 966 | from typing import assert_type, final, TypeIs |
| 967 | |
| 968 | class Parent: pass |
| 969 | class Child(Parent): pass |
| 970 | @final |
| 971 | class Unrelated: pass |
| 972 | |
| 973 | def is_parent(val: object) -> TypeIs[Parent]: |
| 974 | return isinstance(val, Parent) |
| 975 | |
| 976 | def run(arg: Child | Unrelated): |
| 977 | if is_parent(arg): |
| 978 | # Type of ``arg`` is narrowed to the intersection |
| 979 | # of ``Parent`` and ``Child``, which is equivalent to |
| 980 | # ``Child``. |
| 981 | assert_type(arg, Child) |
| 982 | else: |
| 983 | # Type of ``arg`` is narrowed to exclude ``Parent``, |
| 984 | # so only ``Unrelated`` is left. |
| 985 | assert_type(arg, Unrelated) |
| 986 | |
| 987 | The type inside ``TypeIs`` must be consistent with the type of the |
| 988 | function's argument; if it is not, static type checkers will raise |
| 989 | an error. An incorrectly written ``TypeIs`` function can lead to |
searching dependent graphs…