66"""
77
88import textwrap
9- from typing import Literal , Never , NotRequired , Required , TypedDict
9+ from typing import Literal , Never , NotRequired , Required , TypedDict , Union
1010
1111import typemap_extensions as typing
1212from typemap .type_eval import eval_typing
@@ -50,6 +50,7 @@ class TodoTD(TypedDict):
5050
5151# Omit<T, Keys>
5252# Constructs a type by picking all properties from T and then removing Keys.
53+ # Note that unlike in TS, our Omit does not depend on Exclude.
5354type Omit [T , Keys ] = typing .NewProtocol [
5455 * [
5556 p
@@ -58,6 +59,27 @@ class TodoTD(TypedDict):
5859 ]
5960]
6061
62+ # Exclude<T, U>
63+ # Constructs a type by excluding from T all union members assignable to U.
64+ type Exclude [T , U ] = Union [
65+ * [
66+ x
67+ for x in typing .Iter [typing .FromUnion [T ]]
68+ if not typing .IsAssignable [x , U ]
69+ ]
70+ ]
71+
72+ # Extract<T, U>
73+ # Constructs a type by extracting from T all union members assignable to U.
74+ type Extract [T , U ] = Union [
75+ * [
76+ x
77+ for x in typing .Iter [typing .FromUnion [T ]]
78+ # Just the inverse of Exclude, really
79+ if typing .IsAssignable [x , U ]
80+ ]
81+ ]
82+
6183# Partial<T>
6284# Constructs a type with all properties of T set to optional (T | None).
6385type Partial [T ] = typing .NewProtocol [
@@ -156,3 +178,28 @@ def test_td_total_false_inherited():
156178 assert quals ["y" ] is Never
157179 # z defined in total=True child -> no qual
158180 assert quals ["z" ] is Never
181+
182+
183+ def test_exclude ():
184+ # bool is assignable to int, so it gets excluded too
185+ assert eval_typing (Exclude [str | int | bool , int ]) is str
186+
187+ assert eval_typing (
188+ Exclude [Literal ["a" ] | Literal ["b" ] | Literal ["c" ], Literal ["a" ]]
189+ ) == (Literal ["b" ] | Literal ["c" ])
190+
191+ assert eval_typing (Exclude [str | int | float , str | int ]) is float
192+
193+
194+ def test_extract ():
195+ # bool is assignable to int, so it gets extracted
196+ assert eval_typing (Extract [str | int | bool , int ]) == (int | bool )
197+
198+ assert (
199+ eval_typing (
200+ Extract [Literal ["a" ] | Literal ["b" ] | Literal ["c" ], Literal ["a" ]]
201+ )
202+ == Literal ["a" ]
203+ )
204+
205+ assert eval_typing (Extract [str | int | float , str | int ]) == (str | int )
0 commit comments