diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake
index 98e8a84608a56..b8f2b36f324fd 100644
--- a/extmod/extmod.cmake
+++ b/extmod/extmod.cmake
@@ -43,6 +43,7 @@ set(MICROPY_SOURCE_EXTMOD
     ${MICROPY_EXTMOD_DIR}/modtls_axtls.c
     ${MICROPY_EXTMOD_DIR}/modtls_mbedtls.c
     ${MICROPY_EXTMOD_DIR}/modtime.c
+    ${MICROPY_EXTMOD_DIR}/modtyping.c
     ${MICROPY_EXTMOD_DIR}/modvfs.c
     ${MICROPY_EXTMOD_DIR}/modwebsocket.c
     ${MICROPY_EXTMOD_DIR}/modwebrepl.c
diff --git a/extmod/extmod.mk b/extmod/extmod.mk
index c132fd89ce887..f7acc0fe0251a 100644
--- a/extmod/extmod.mk
+++ b/extmod/extmod.mk
@@ -43,6 +43,7 @@ SRC_EXTMOD_C += \
 	extmod/modtls_axtls.c \
 	extmod/modtls_mbedtls.c \
 	extmod/modtime.c \
+	extmod/modtyping.c \
 	extmod/moductypes.c \
 	extmod/modvfs.c \
 	extmod/modwebrepl.c \
diff --git a/extmod/modtyping.c b/extmod/modtyping.c
new file mode 100644
index 0000000000000..8bf3865712a89
--- /dev/null
+++ b/extmod/modtyping.c
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/obj.h"
+
+#if MICROPY_PY_TYPING
+
+// Implement roughly the equivalent of the following minimal Python typing module, meant to support the
+// typing syntax at runtime but otherwise ignoring any functionality:
+//
+//  class _AnyCall:
+//    def __init__(*args, **kwargs):
+//        pass
+//
+//    def __call__(self, *args, **kwargs):
+//        return self
+//
+//    def __getitem__(self, attr):
+//        return self
+//
+//  _typing_obj = _AnyCall()
+//
+//  def __getattr__(attr):
+//      return _typing_obj
+//
+// Note this works together with the micropython compiler itself ignoring type hints, i.e. when encountering
+//
+// def hello(name: str) -> None:
+//    pass
+//
+// both str and None hints are simply ignored.
+
+
+// The singleton object returned everywhere, both for module attr/subscr as well as for its own
+// attr/subscr calls.
+extern mp_obj_base_t typing_obj;
+
+static mp_obj_t any_call_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
+    if (value == MP_OBJ_NULL) {
+        return MP_OBJ_NULL;
+    }
+    return self_in;
+}
+
+// Can be used both for __new__ and __call__: the latter's prototype is
+// (mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args)
+// so this function works as long as the argument size matches.
+static mp_obj_t any_call_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    #if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
+    MP_STATIC_ASSERT(sizeof(mp_obj_type_t *) == sizeof(mp_obj_t));
+    #endif
+    return MP_OBJ_FROM_PTR(&typing_obj);
+}
+
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+static mp_obj_t any_call_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    return MP_OBJ_FROM_PTR(&typing_obj);
+}
+#else
+#define any_call_call any_call_new
+#endif
+
+#if defined(MICROPY_UNIX_COVERAGE)
+void any_call_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
+#endif
+
+// Not static because it's also used for the module's attr delegation.
+void any_call_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+    // Only loading is supported.
+    if (dest[ 0 ] == MP_OBJ_NULL) {
+        dest[ 0 ] = MP_OBJ_FROM_PTR(&typing_obj);
+    }
+}
+
+
+static MP_DEFINE_CONST_OBJ_TYPE(
+    mp_type_any_call_t,
+    MP_QSTR_any_call,
+    MP_TYPE_FLAG_NONE,
+    make_new,
+    any_call_new,
+    attr,
+    any_call_attr,
+    subscr,
+    any_call_subscr,
+    call,
+    any_call_call
+    );
+
+mp_obj_base_t typing_obj = {&mp_type_any_call_t};
+
+
+static const mp_rom_map_elem_t mp_module_typing_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_typing) },
+};
+
+static MP_DEFINE_CONST_DICT(mp_module_typing_globals, mp_module_typing_globals_table);
+
+const mp_obj_module_t mp_module_typing = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t *)&mp_module_typing_globals,
+};
+
+// Extensible such that a typing module implemented in Python still has priority.
+MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_typing, mp_module_typing);
+MP_REGISTER_MODULE_DELEGATION(mp_module_typing, any_call_attr);
+
+
+#if MICROPY_PY_TYPING_EXTRA_MODULES
+MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_abc, mp_module_typing);
+MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_typing_extensions, mp_module_typing);
+#endif
+
+#endif // MICROPY_PY_TYPING
diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h
index cea0397414325..f4afebff437a0 100644
--- a/ports/unix/variants/mpconfigvariant_common.h
+++ b/ports/unix/variants/mpconfigvariant_common.h
@@ -121,3 +121,7 @@
 #define MICROPY_PY_MACHINE             (1)
 #define MICROPY_PY_MACHINE_PULSE       (1)
 #define MICROPY_PY_MACHINE_PIN_BASE    (1)
+
+// Enable "typing" and related modules.
+#define MICROPY_PY_TYPING               (1)
+#define MICROPY_PY_TYPING_EXTRA_MODULES (1)
diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h
index fabc9072d6c70..cfe08024f6fc7 100644
--- a/ports/windows/mpconfigport.h
+++ b/ports/windows/mpconfigport.h
@@ -143,6 +143,8 @@
 #define MICROPY_PY_TIME_TIME_TIME_NS (1)
 #define MICROPY_PY_TIME_CUSTOM_SLEEP (1)
 #define MICROPY_PY_TIME_INCLUDEFILE "ports/unix/modtime.c"
+#define MICROPY_PY_TYPING           (1)
+#define MICROPY_PY_TYPING_EXTRA_MODULES (1)
 #define MICROPY_PY_ERRNO            (1)
 #define MICROPY_PY_UCTYPES          (1)
 #define MICROPY_PY_DEFLATE          (1)
diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props
index f7c4c6bcac01b..859f8e8670911 100644
--- a/ports/windows/msvc/sources.props
+++ b/ports/windows/msvc/sources.props
@@ -20,6 +20,7 @@
     <PyExtModSource Include="$(PyBaseDir)extmod\modre.c" />
     <PyExtModSource Include="$(PyBaseDir)extmod\modselect.c" />
     <PyExtModSource Include="$(PyBaseDir)extmod\modtime.c" />
+    <PyExtModSource Include="$(PyBaseDir)extmod\modtyping.c" />
     <PyExtModSource Include="$(PyBaseDir)extmod\virtpin.c" />
     <PyExtModSource Include="$(PyBaseDir)extmod\vfs.c" />
     <PyExtModSource Include="$(PyBaseDir)extmod\vfs_posix.c" />
diff --git a/py/mpconfig.h b/py/mpconfig.h
index b5414312c7caf..54589de41e92b 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -1626,6 +1626,17 @@ typedef double mp_float_t;
 #define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32)
 #endif
 
+// Whether to provide the minimal typing module.
+#ifndef MICROPY_PY_TYPING
+#define MICROPY_PY_TYPING (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
+#endif
+
+// Whether to provide the minimal abc and typing_extensions modules.
+// They will simply be aliases for the typing module.
+#ifndef MICROPY_PY_TYPING_EXTRA_MODULES
+#define MICROPY_PY_TYPING_EXTRA_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
+#endif
+
 // Extended modules
 
 #ifndef MICROPY_PY_ASYNCIO
diff --git a/tests/extmod/typing_syntax.py b/tests/extmod/typing_syntax.py
new file mode 100644
index 0000000000000..bb4d5c63e7ed7
--- /dev/null
+++ b/tests/extmod/typing_syntax.py
@@ -0,0 +1,40 @@
+# This doesn't quite test everything but just serves to verify that basic syntax works,
+# which for MicroPython means everything typing-related should be ignored.
+
+try:
+    import typing
+except ImportError:
+    print("SKIP")
+    raise SystemExit
+
+from typing import List, Tuple, Iterable, NewType, TypeVar, Union, Generic
+
+# Available with MICROPY_PY_TYPING_EXTRA_MODULES.
+try:
+    from typing_extensions import Any
+except ImportError:
+    from typing import Any
+
+
+MyAlias = str
+Vector: typing.List[float]
+Nested = Iterable[Tuple[MyAlias, ...]]
+UserId = NewType("UserId", int)
+T = TypeVar("T", int, float, complex)
+
+hintedGlobal: Any = None
+
+
+def func_with_hints(c: int, b: MyAlias, a: Union[int, None], lst: List[float] = [0.0]) -> Any:
+    pass
+
+
+class ClassWithHints(Generic[T]):
+    a: int = 0
+
+    def foo(self, other: int) -> None:
+        self.typed_thing: List[T] = []
+
+
+class Bar(ClassWithHints[Any]):
+    pass
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index 176db8e9f8479..8c8acb23cfa72 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -51,15 +51,16 @@ ame__
 port 
 
 builtins        micropython     _asyncio        _thread
-array           binascii        btree           cexample
-cmath           collections     cppexample      cryptolib
-deflate         errno           example_package
+abc             array           binascii        btree
+cexample        cmath           collections     cppexample
+cryptolib       deflate         errno           example_package
 ffi             framebuf        gc              hashlib
 heapq           io              json            machine
 math            os              platform        random
 re              select          socket          struct
 sys             termios         time            tls
-uctypes         vfs             websocket
+typing          typing_extensions               uctypes
+vfs             websocket
 me
 
 micropython     machine         math