diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b87459eb086ae5ca7244efe6e64cfdbd87e75cd..264fe6f7c427835ebe76b1b6358f3d4c28c8ee4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
 
+### Added
+- Add type_info helpers ([#502](https://github.com/stack-of-tasks/eigenpy/pull/502))
+
 ## [3.9.0] - 2024-08-31
 
 ### Changed
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 56864d6b60b7053d98a57851461170fe4173cb09..ed103c4328a2a9e385ea7657cd9a64ad1c99534c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -275,6 +275,7 @@ set(${PROJECT_NAME}_HEADERS
     include/eigenpy/variant.hpp
     include/eigenpy/std-unique-ptr.hpp
     include/eigenpy/swig.hpp
+    include/eigenpy/type_info.hpp
     include/eigenpy/version.hpp)
 
 list(
@@ -349,6 +350,7 @@ set(${PROJECT_NAME}_SOURCES
     src/scipy-type.cpp
     src/std-vector.cpp
     src/optional.cpp
+    src/type_info.cpp
     src/version.cpp)
 
 add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES}
diff --git a/include/eigenpy/type_info.hpp b/include/eigenpy/type_info.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6601677a1875025d64cd978ebbd26f9ca34ed919
--- /dev/null
+++ b/include/eigenpy/type_info.hpp
@@ -0,0 +1,77 @@
+///
+/// Copyright (c) 2024 INRIA
+///
+
+#ifndef __eigenpy_type_info_hpp__
+#define __eigenpy_type_info_hpp__
+
+#include "eigenpy/fwd.hpp"
+
+#include <boost/type_index.hpp>
+#include <typeinfo>
+#include <typeindex>
+
+namespace eigenpy {
+
+template <typename T>
+boost::typeindex::type_index type_info(const T& value) {
+  return boost::typeindex::type_id_runtime(value);
+}
+
+template <typename T>
+void expose_boost_type_info() {
+  boost::python::def(
+      "type_info",
+      +[](const T& value) -> boost::typeindex::type_index {
+        return boost::typeindex::type_id_runtime(value);
+      },
+      bp::arg("value"),
+      "Returns information of the type of value as a "
+      "boost::typeindex::type_index (can work without RTTI).");
+  boost::python::def(
+      "boost_type_info",
+      +[](const T& value) -> boost::typeindex::type_index {
+        return boost::typeindex::type_id_runtime(value);
+      },
+      bp::arg("value"),
+      "Returns information of the type of value as a "
+      "boost::typeindex::type_index (can work without RTTI).");
+}
+
+template <typename T>
+void expose_std_type_info() {
+  boost::python::def(
+      "std_type_info",
+      +[](const T& value) -> std::type_index { return typeid(value); },
+      bp::arg("value"),
+      "Returns information of the type of value as a std::type_index.");
+}
+
+///
+/// \brief Add the Python method type_info to query information of a type.
+///
+template <class C>
+struct TypeInfoVisitor : public bp::def_visitor<TypeInfoVisitor<C> > {
+  template <class PyClass>
+  void visit(PyClass& cl) const {
+    cl.def("type_info", &boost_type_info, bp::arg("self"),
+           "Queries information of the type of *this as a "
+           "boost::typeindex::type_index (can work without RTTI).");
+    cl.def("boost_type_info", &boost_type_info, bp::arg("self"),
+           "Queries information of the type of *this as a "
+           "boost::typeindex::type_index (can work without RTTI).");
+    cl.def("std_type_info", &std_type_info, bp::arg("self"),
+           "Queries information of the type of *this as a std::type_index.");
+  }
+
+ private:
+  static boost::typeindex::type_index boost_type_info(const C& self) {
+    return boost::typeindex::type_id_runtime(self);
+  }
+
+  static std::type_index std_type_info(const C& self) { return typeid(self); }
+};
+
+}  // namespace eigenpy
+
+#endif  // __eigenpy_type_info_hpp__
diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp
index cc2dcefbca0b3bfca2d5c685cb252b29fa3ff76a..93a4b9eb0b77d552c027afefa40f695d73889c76 100644
--- a/src/eigenpy.cpp
+++ b/src/eigenpy.cpp
@@ -36,6 +36,7 @@ void exposeMatrixComplexDouble();
 void exposeMatrixComplexLongDouble();
 
 void exposeNoneType();
+void exposeTypeInfo();
 
 /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instances.
  */
@@ -84,6 +85,7 @@ void enableEigenPy() {
   exposeMatrixComplexLongDouble();
 
   exposeNoneType();
+  exposeTypeInfo();
 }
 
 bool withTensorSupport() {
diff --git a/src/type_info.cpp b/src/type_info.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..482370cea4ca7909d1e65825d3ed4b8021a46408
--- /dev/null
+++ b/src/type_info.cpp
@@ -0,0 +1,81 @@
+///
+/// Copyright 2024 INRIA
+///
+
+#include <typeinfo>
+#include <typeindex>
+
+#include <boost/python.hpp>
+#include <boost/type_index.hpp>
+
+#include "eigenpy/registration.hpp"
+
+namespace bp = boost::python;
+
+namespace eigenpy {
+
+void exposeStdTypeIndex() {
+  typedef std::type_index Self;
+  if (register_symbolic_link_to_registered_type<Self>()) return;
+
+  bp::class_<Self>(
+      "std_type_index",
+      "The class type_index holds implementation-specific information about a "
+      "type, including the name of the type and means to compare two types for "
+      "equality or collating order.",
+      bp::no_init)
+      .def(bp::self == bp::self)
+      .def(bp::self >= bp::self)
+      .def(bp::self > bp::self)
+      .def(bp::self < bp::self)
+      .def(bp::self <= bp::self)
+      .def("hash_code", &Self::hash_code, bp::arg("self"),
+           "Returns an unspecified value (here denoted by hash code) such that "
+           "for all std::type_info objects referring to the same type, their "
+           "hash code is the same.")
+      .def("name", &Self::name, bp::arg("self"),
+           "Returns an implementation defined null-terminated character string "
+           "containing the name of the type. No guarantees are given; in "
+           "particular, the returned string can be identical for several types "
+           "and change between invocations of the same program.")
+      .def(
+          "pretty_name",
+          +[](const Self &value) -> std::string {
+            return boost::core::demangle(value.name());
+          },
+          bp::arg("self"), "Human readible name.");
+}
+
+void exposeBoostTypeIndex() {
+  typedef boost::typeindex::type_index Self;
+  if (register_symbolic_link_to_registered_type<Self>()) return;
+
+  bp::class_<Self>(
+      "boost_type_index",
+      "The class type_index holds implementation-specific information about a "
+      "type, including the name of the type and means to compare two types for "
+      "equality or collating order.",
+      bp::no_init)
+      .def(bp::self == bp::self)
+      .def(bp::self >= bp::self)
+      .def(bp::self > bp::self)
+      .def(bp::self < bp::self)
+      .def(bp::self <= bp::self)
+      .def("hash_code", &Self::hash_code, bp::arg("self"),
+           "Returns an unspecified value (here denoted by hash code) such that "
+           "for all std::type_info objects referring to the same type, their "
+           "hash code is the same.")
+      .def("name", &Self::name, bp::arg("self"),
+           "Returns an implementation defined null-terminated character string "
+           "containing the name of the type. No guarantees are given; in "
+           "particular, the returned string can be identical for several types "
+           "and change between invocations of the same program.")
+      .def("pretty_name", &Self::pretty_name, bp::arg("self"),
+           "Human readible name.");
+}
+
+void exposeTypeInfo() {
+  exposeStdTypeIndex();
+  exposeBoostTypeIndex();
+}
+}  // namespace eigenpy
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt
index 2c1eec31b35a58ef3927ede297b7619d4b60025c..98485fbedfee1f04bc8471554ab9d71af10c1d19 100644
--- a/unittest/CMakeLists.txt
+++ b/unittest/CMakeLists.txt
@@ -35,6 +35,7 @@ endfunction()
 
 add_dependencies(build_tests ${PYWRAP})
 add_lib_unit_test(matrix)
+add_lib_unit_test(type_info)
 add_lib_unit_test(multiple_registration)
 if(BUILD_TESTING_SCIPY)
   find_scipy()
@@ -111,6 +112,7 @@ endif()
 add_lib_unit_test(bind_virtual_factory)
 
 add_python_lib_unit_test("py-matrix" "unittest/python/test_matrix.py")
+add_python_lib_unit_test("py-type-info" "unittest/python/test_type_info.py")
 add_python_lib_unit_test("py-multiple-registration"
                          "unittest/python/test_multiple_registration.py")
 
diff --git a/unittest/python/test_type_info.py b/unittest/python/test_type_info.py
new file mode 100644
index 0000000000000000000000000000000000000000..09de559289e58e721ac63b393994b6de0fcf0216
--- /dev/null
+++ b/unittest/python/test_type_info.py
@@ -0,0 +1,7 @@
+import type_info
+
+d = type_info.Dummy()
+assert "Dummy" in d.type_info().pretty_name()
+
+assert type_info.type_info(1).pretty_name() == "int"
+assert "basic_string" in type_info.type_info("toto").pretty_name()
diff --git a/unittest/type_info.cpp b/unittest/type_info.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..35e410cfc7640ce39f3feb099ab0373b7c5405db
--- /dev/null
+++ b/unittest/type_info.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include <iostream>
+
+#include "eigenpy/eigenpy.hpp"
+#include "eigenpy/type_info.hpp"
+
+struct Dummy {};
+
+BOOST_PYTHON_MODULE(type_info) {
+  using namespace Eigen;
+  namespace bp = boost::python;
+  eigenpy::enableEigenPy();
+
+  eigenpy::expose_boost_type_info<int>();
+  eigenpy::expose_boost_type_info<std::string>();
+
+  bp::class_<Dummy>("Dummy").def(eigenpy::TypeInfoVisitor<Dummy>());
+}