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)
| 882 | |
| 883 | @_SpecialForm |
| 884 | def TypeIs(self, parameters): |
| 885 | """Special typing construct for marking user-defined type predicate functions. |
| 886 | |
| 887 | ``TypeIs`` can be used to annotate the return type of a user-defined |
| 888 | type predicate function. ``TypeIs`` only accepts a single type argument. |
| 889 | At runtime, functions marked this way should return a boolean and accept |
| 890 | at least one argument. |
| 891 | |
| 892 | ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static |
| 893 | type checkers to determine a more precise type of an expression within a |
| 894 | program's code flow. Usually type narrowing is done by analyzing |
| 895 | conditional code flow and applying the narrowing to a block of code. The |
| 896 | conditional expression here is sometimes referred to as a "type predicate". |
| 897 | |
| 898 | Sometimes it would be convenient to use a user-defined boolean function |
| 899 | as a type predicate. Such a function should use ``TypeIs[...]`` or |
| 900 | ``TypeGuard[...]`` as its return type to alert static type checkers to |
| 901 | this intention. ``TypeIs`` usually has more intuitive behavior than |
| 902 | ``TypeGuard``, but it cannot be used when the input and output types |
| 903 | are incompatible (e.g., ``list[object]`` to ``list[int]``) or when the |
| 904 | function does not return ``True`` for all instances of the narrowed type. |
| 905 | |
| 906 | Using ``-> TypeIs[NarrowedType]`` tells the static type checker that for |
| 907 | a given function: |
| 908 | |
| 909 | 1. The return value is a boolean. |
| 910 | 2. If the return value is ``True``, the type of its argument |
| 911 | is the intersection of the argument's original type and |
| 912 | ``NarrowedType``. |
| 913 | 3. If the return value is ``False``, the type of its argument |
| 914 | is narrowed to exclude ``NarrowedType``. |
| 915 | |
| 916 | For example:: |
| 917 | |
| 918 | from typing import assert_type, final, TypeIs |
| 919 | |
| 920 | class Parent: pass |
| 921 | class Child(Parent): pass |
| 922 | @final |
| 923 | class Unrelated: pass |
| 924 | |
| 925 | def is_parent(val: object) -> TypeIs[Parent]: |
| 926 | return isinstance(val, Parent) |
| 927 | |
| 928 | def run(arg: Child | Unrelated): |
| 929 | if is_parent(arg): |
| 930 | # Type of ``arg`` is narrowed to the intersection |
| 931 | # of ``Parent`` and ``Child``, which is equivalent to |
| 932 | # ``Child``. |
| 933 | assert_type(arg, Child) |
| 934 | else: |
| 935 | # Type of ``arg`` is narrowed to exclude ``Parent``, |
| 936 | # so only ``Unrelated`` is left. |
| 937 | assert_type(arg, Unrelated) |
| 938 | |
| 939 | The type inside ``TypeIs`` must be consistent with the type of the |
| 940 | function's argument; if it is not, static type checkers will raise |
| 941 | an error. An incorrectly written ``TypeIs`` function can lead to |