diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index a71df9a3d662ee01d95e40366faeaadc081ea561..74806be26a1d95f07a33cf682f78a407a11845d5 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -3,6 +3,7 @@
 #include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/OperatorRepository.hpp>
 #include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <utils/PugsAssert.hpp>
@@ -352,10 +353,20 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
       ASTNodeNaturalConversionChecker{test_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()};
 
     } else if (n.is_type<language::unary_not>()) {
-      n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+      auto& operator_repository = OperatorRepository::instance();
+
+      auto optional_value_type = operator_repository.getUnaryOperatorValueType(
+        unaryOperatorMangler<language::unary_not>(n.children[0]->m_data_type));
 
-      const ASTNode& operand_node = *n.children[0];
-      ASTNodeNaturalConversionChecker{operand_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()};
+      if (optional_value_type.has_value()) {
+        n.m_data_type = optional_value_type.value();
+      } else {
+        std::ostringstream message;
+        message << "undefined unary operator\n"
+                << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type)
+                << rang::style::reset;
+        throw ParseError(message.str(), n.begin());
+      }
 
     } else if (n.is_type<language::lesser_op>() or n.is_type<language::lesser_or_eq_op>() or
                n.is_type<language::greater_op>() or n.is_type<language::greater_or_eq_op>() or
@@ -371,15 +382,54 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
       ASTNodeNaturalConversionChecker{rhs_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()};
 
     } else if (n.is_type<language::unary_minus>()) {
-      n.m_data_type = n.children[0]->m_data_type;
-      if ((n.children[0]->m_data_type == ASTNodeDataType::unsigned_int_t) or
-          (n.children[0]->m_data_type == ASTNodeDataType::bool_t)) {
-        n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+      auto& operator_repository = OperatorRepository::instance();
+
+      auto optional_value_type = operator_repository.getUnaryOperatorValueType(
+        unaryOperatorMangler<language::unary_minus>(n.children[0]->m_data_type));
+
+      if (optional_value_type.has_value()) {
+        n.m_data_type = optional_value_type.value();
       } else {
-        n.m_data_type = n.children[0]->m_data_type;
+        std::ostringstream message;
+        message << "undefined unary operator\n"
+                << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type)
+                << rang::style::reset;
+        throw ParseError(message.str(), n.begin());
       }
     } else if (n.is_type<language::unary_plusplus>() or n.is_type<language::unary_minusminus>() or
                n.is_type<language::post_plusplus>() or n.is_type<language::post_minusminus>()) {
+      auto& operator_repository = OperatorRepository::instance();
+
+      auto optional_value_type = [&] {
+        if (n.is_type<language::unary_plusplus>()) {
+          return operator_repository.getIncDecOperatorValueType(
+            incDecOperatorMangler<language::unary_plusplus>(n.children[0]->m_data_type));
+        } else if (n.is_type<language::unary_minusminus>()) {
+          return operator_repository.getIncDecOperatorValueType(
+            incDecOperatorMangler<language::unary_minusminus>(n.children[0]->m_data_type));
+        } else if (n.is_type<language::post_minusminus>()) {
+          return operator_repository.getIncDecOperatorValueType(
+            incDecOperatorMangler<language::post_minusminus>(n.children[0]->m_data_type));
+        } else if (n.is_type<language::post_plusplus>()) {
+          return operator_repository.getIncDecOperatorValueType(
+            incDecOperatorMangler<language::post_plusplus>(n.children[0]->m_data_type));
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("unexpected operator type");
+          // LCOV_EXCL_STOP
+        }
+      }();
+
+      if (optional_value_type.has_value()) {
+        n.m_data_type = optional_value_type.value();
+      } else {
+        std::ostringstream message;
+        message << "undefined unary operator\n"
+                << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type)
+                << rang::style::reset;
+        throw ParseError(message.str(), n.begin());
+      }
+
       n.m_data_type = n.children[0]->m_data_type;
     } else if (n.is_type<language::plus_op>() or n.is_type<language::minus_op>() or
                n.is_type<language::multiply_op>() or n.is_type<language::divide_op>()) {
@@ -393,8 +443,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
       if (n.m_data_type == ASTNodeDataType::undefined_t) {
         std::ostringstream message;
         message << "undefined binary operator\n"
-                << "note: incompatible operand types " << n.children[0]->string() << " (" << dataTypeName(type_0)
-                << ") and " << n.children[1]->string() << " (" << dataTypeName(type_1) << ')';
+                << "note: incompatible operand types " << dataTypeName(type_0) << " and " << dataTypeName(type_1);
         throw ParseError(message.str(), n.begin());
       }
     } else if (n.is_type<language::function_evaluation>()) {
diff --git a/src/language/utils/IIncDecOperatorProcessorBuilder.hpp b/src/language/utils/IIncDecOperatorProcessorBuilder.hpp
index 050bb60f37fd4ada7c7649e6efbd83eb96e90373..918cef6a047828459a9b2937823e99308dcd137c 100644
--- a/src/language/utils/IIncDecOperatorProcessorBuilder.hpp
+++ b/src/language/utils/IIncDecOperatorProcessorBuilder.hpp
@@ -3,6 +3,7 @@
 
 class ASTNode;
 class INodeProcessor;
+#include <language/utils/ASTNodeDataType.hpp>
 
 #include <memory>
 
@@ -11,6 +12,8 @@ class IIncDecOperatorProcessorBuilder
  public:
   virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const = 0;
 
+  virtual ASTNodeDataType getReturnValueType() const = 0;
+
   virtual ~IIncDecOperatorProcessorBuilder() = default;
 };
 
diff --git a/src/language/utils/IUnaryOperatorProcessorBuilder.hpp b/src/language/utils/IUnaryOperatorProcessorBuilder.hpp
index 7aca7c2c26d4c004d1977e10ca51aee290bea5b6..b56ca3f466c1eb8ca886a20b64df4cbb7ea3b003 100644
--- a/src/language/utils/IUnaryOperatorProcessorBuilder.hpp
+++ b/src/language/utils/IUnaryOperatorProcessorBuilder.hpp
@@ -4,6 +4,8 @@
 class ASTNode;
 class INodeProcessor;
 
+#include <language/utils/ASTNodeDataType.hpp>
+
 #include <memory>
 
 class IUnaryOperatorProcessorBuilder
@@ -11,6 +13,8 @@ class IUnaryOperatorProcessorBuilder
  public:
   virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const = 0;
 
+  virtual ASTNodeDataType getReturnValueType() const = 0;
+
   virtual ~IUnaryOperatorProcessorBuilder() = default;
 };
 
diff --git a/src/language/utils/IncDecOperatorProcessorBuilder.hpp b/src/language/utils/IncDecOperatorProcessorBuilder.hpp
index c3eeb10baa44d6668b0ac6e88e9f622aea9a827a..40da865ddf9c6ed480a859e9a158d21cb5562848 100644
--- a/src/language/utils/IncDecOperatorProcessorBuilder.hpp
+++ b/src/language/utils/IncDecOperatorProcessorBuilder.hpp
@@ -4,6 +4,7 @@
 #include <algebra/TinyVector.hpp>
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/IncDecExpressionProcessor.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <language/utils/IIncDecOperatorProcessorBuilder.hpp>
 
 #include <type_traits>
@@ -14,6 +15,12 @@ class IncDecOperatorProcessorBuilder final : public IIncDecOperatorProcessorBuil
  public:
   IncDecOperatorProcessorBuilder() = default;
 
+  ASTNodeDataType
+  getReturnValueType() const
+  {
+    return ast_node_data_type_from<DataT>;
+  }
+
   std::unique_ptr<INodeProcessor>
   getNodeProcessor(ASTNode& node) const
   {
diff --git a/src/language/utils/OperatorRepository.hpp b/src/language/utils/OperatorRepository.hpp
index ae6b2a9844fa67fa8cdae7f1a55115c56126a30c..e7c1ea2f3a9bfc7ee21ddae4977f688894ae909a 100644
--- a/src/language/utils/OperatorRepository.hpp
+++ b/src/language/utils/OperatorRepository.hpp
@@ -17,74 +17,144 @@
 class OperatorRepository
 {
  private:
-  std::unordered_map<std::string, std::shared_ptr<const IAffectationProcessorBuilder>> m_affectation_builder_list;
-  std::unordered_map<std::string, std::shared_ptr<const IIncDecOperatorProcessorBuilder>>
-    m_inc_dec_operator_builder_list;
-  std::unordered_map<std::string, std::shared_ptr<const IUnaryOperatorProcessorBuilder>> m_unary_operator_builder_list;
+  template <typename ProcessorBuilderT>
+  class Descriptor
+  {
+   private:
+    ASTNodeDataType m_value_type;
+    std::shared_ptr<const ProcessorBuilderT> m_processor_builder;
+
+   public:
+    const ASTNodeDataType&
+    valueType() const
+    {
+      return m_value_type;
+    }
+
+    const std::shared_ptr<const ProcessorBuilderT>&
+    processorBuilder() const
+    {
+      return m_processor_builder;
+    }
+
+    Descriptor(const ASTNodeDataType& value_type, const std::shared_ptr<const ProcessorBuilderT>& processor_builder)
+      : m_value_type{value_type}, m_processor_builder{processor_builder}
+    {}
+
+    Descriptor(const Descriptor&) = default;
+    Descriptor(Descriptor&&)      = default;
+    Descriptor()                  = default;
+    ~Descriptor()                 = default;
+  };
+
+  std::unordered_map<std::string, Descriptor<IAffectationProcessorBuilder>> m_affectation_builder_list;
+
+  std::unordered_map<std::string, Descriptor<IIncDecOperatorProcessorBuilder>> m_inc_dec_operator_builder_list;
+
+  std::unordered_map<std::string, Descriptor<IUnaryOperatorProcessorBuilder>> m_unary_operator_builder_list;
 
   void _initialize();
 
  public:
   void reset();
 
-  template <typename OperatorTypeT, typename AffectationProcessorBuilderT>
+  template <typename OperatorTypeT>
   void
-  addAffectation(const ASTNodeDataType& lhs,
-                 const ASTNodeDataType& rhs,
-                 const std::shared_ptr<AffectationProcessorBuilderT>& processor_builder)
+  addAffectation(const ASTNodeDataType& lhs_type,
+                 const ASTNodeDataType& rhs_type,
+                 const std::shared_ptr<const IAffectationProcessorBuilder>& processor_builder)
   {
-    const std::string affectation_type_name = affectationMangler<OperatorTypeT>(lhs, rhs);
-    if (not m_affectation_builder_list.try_emplace(affectation_type_name, processor_builder).second) {
+    const std::string affectation_type_name = affectationMangler<OperatorTypeT>(lhs_type, rhs_type);
+    if (not m_affectation_builder_list
+              .try_emplace(affectation_type_name,
+                           Descriptor{ASTNodeDataType::build<ASTNodeDataType::void_t>(), processor_builder})
+              .second) {
+      // LCOV_EXCL_START
       throw UnexpectedError(affectation_type_name + " has already an entry");
+      // LCOV_EXCL_STOP
     }
   }
 
-  template <typename OperatorTypeT, typename IncDecProcessorBuilderT>
+  template <typename OperatorTypeT>
   void
-  addIncDecOperator(const ASTNodeDataType& operand, const std::shared_ptr<IncDecProcessorBuilderT>& processor_builder)
+  addIncDecOperator(const ASTNodeDataType& operand_type,
+                    const std::shared_ptr<const IIncDecOperatorProcessorBuilder>& processor_builder)
   {
-    const std::string inc_dec_operator_type_name = incDecOperatorMangler<OperatorTypeT>(operand);
-    if (not m_inc_dec_operator_builder_list.try_emplace(inc_dec_operator_type_name, processor_builder).second) {
+    const std::string inc_dec_operator_type_name = incDecOperatorMangler<OperatorTypeT>(operand_type);
+    if (auto [i_descriptor, success] =
+          m_inc_dec_operator_builder_list.try_emplace(inc_dec_operator_type_name,
+                                                      Descriptor{processor_builder->getReturnValueType(),
+                                                                 processor_builder});
+        not success) {
+      // LCOV_EXCL_START
       throw UnexpectedError(inc_dec_operator_type_name + " has already an entry");
+      // LCOV_EXCL_STOP
     }
   }
 
-  template <typename OperatorTypeT, typename UnaryProcessorBuilderT>
+  template <typename OperatorTypeT>
   void
-  addUnaryOperator(const ASTNodeDataType& operand, const std::shared_ptr<UnaryProcessorBuilderT>& processor_builder)
+  addUnaryOperator(const ASTNodeDataType& operand_type,
+                   const std::shared_ptr<const IUnaryOperatorProcessorBuilder>& processor_builder)
   {
-    const std::string unary_operator_type_name = unaryOperatorMangler<OperatorTypeT>(operand);
-    if (not m_unary_operator_builder_list.try_emplace(unary_operator_type_name, processor_builder).second) {
+    const std::string unary_operator_type_name = unaryOperatorMangler<OperatorTypeT>(operand_type);
+    if (auto [i_descriptor, success] =
+          m_unary_operator_builder_list.try_emplace(unary_operator_type_name,
+                                                    Descriptor{processor_builder->getReturnValueType(),
+                                                               processor_builder});
+        not success) {
+      // LCOV_EXCL_START
       throw UnexpectedError(unary_operator_type_name + " has already an entry");
+      // LCOV_EXCL_STOP
     }
   }
 
-  std::optional<std::shared_ptr<const IAffectationProcessorBuilder>>
+  [[nodiscard]] std::optional<std::shared_ptr<const IAffectationProcessorBuilder>>
   getAffectationProcessorBuilder(const std::string& name) const
   {
     auto&& processor_builder = m_affectation_builder_list.find(name);
     if (processor_builder != m_affectation_builder_list.end()) {
-      return processor_builder->second;
+      return processor_builder->second.processorBuilder();
     }
     return {};
   }
 
-  std::optional<std::shared_ptr<const IIncDecOperatorProcessorBuilder>>
+  [[nodiscard]] std::optional<std::shared_ptr<const IIncDecOperatorProcessorBuilder>>
   getIncDecProcessorBuilder(const std::string& name) const
   {
     auto&& processor_builder = m_inc_dec_operator_builder_list.find(name);
     if (processor_builder != m_inc_dec_operator_builder_list.end()) {
-      return processor_builder->second;
+      return processor_builder->second.processorBuilder();
     }
     return {};
   }
 
-  std::optional<std::shared_ptr<const IUnaryOperatorProcessorBuilder>>
+  [[nodiscard]] std::optional<std::shared_ptr<const IUnaryOperatorProcessorBuilder>>
   getUnaryProcessorBuilder(const std::string& name) const
   {
     auto&& processor_builder = m_unary_operator_builder_list.find(name);
     if (processor_builder != m_unary_operator_builder_list.end()) {
-      return processor_builder->second;
+      return processor_builder->second.processorBuilder();
+    }
+    return {};
+  }
+
+  [[nodiscard]] std::optional<ASTNodeDataType>
+  getIncDecOperatorValueType(const std::string& name) const
+  {
+    auto&& processor_builder = m_inc_dec_operator_builder_list.find(name);
+    if (processor_builder != m_inc_dec_operator_builder_list.end()) {
+      return processor_builder->second.valueType();
+    }
+    return {};
+  }
+
+  [[nodiscard]] std::optional<ASTNodeDataType>
+  getUnaryOperatorValueType(const std::string& name) const
+  {
+    auto&& processor_builder = m_unary_operator_builder_list.find(name);
+    if (processor_builder != m_unary_operator_builder_list.end()) {
+      return processor_builder->second.valueType();
     }
     return {};
   }
diff --git a/src/language/utils/UnaryOperatorProcessorBuilder.hpp b/src/language/utils/UnaryOperatorProcessorBuilder.hpp
index a4a3a22a4d190621016b57abad87ec8aa9e32514..d99d2e17536de2b31a3ac2c48f39800a2461f17d 100644
--- a/src/language/utils/UnaryOperatorProcessorBuilder.hpp
+++ b/src/language/utils/UnaryOperatorProcessorBuilder.hpp
@@ -4,6 +4,7 @@
 #include <algebra/TinyVector.hpp>
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/UnaryExpressionProcessor.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <language/utils/IUnaryOperatorProcessorBuilder.hpp>
 
 #include <type_traits>
@@ -14,6 +15,12 @@ class UnaryOperatorProcessorBuilder final : public IUnaryOperatorProcessorBuilde
  public:
   UnaryOperatorProcessorBuilder() = default;
 
+  ASTNodeDataType
+  getReturnValueType() const
+  {
+    return ast_node_data_type_from<ValueT>;
+  }
+
   std::unique_ptr<INodeProcessor>
   getNodeProcessor(ASTNode& node) const
   {
diff --git a/tests/test_ASTNodeExpressionBuilder.cpp b/tests/test_ASTNodeExpressionBuilder.cpp
index 956b5a2527e8e80bb1cef2287f9b2c72a16768aa..8cca350f1d165303634b86a7958ab55b81c72cb3 100644
--- a/tests/test_ASTNodeExpressionBuilder.cpp
+++ b/tests/test_ASTNodeExpressionBuilder.cpp
@@ -373,7 +373,10 @@ let n:N;
 
     SECTION("unary not")
     {
-      CHECK_AST_THROWS_WITH(R"(not 1;)", "invalid implicit conversion: Z -> B");
+      std::string error_message = R"(undefined unary operator
+note: unexpected operand type Z)";
+
+      CHECK_AST_THROWS_WITH(R"(not 1;)", error_message);
     }
 
     SECTION("pre-increment operator")
diff --git a/tests/test_ASTNodeIncDecExpressionBuilder.cpp b/tests/test_ASTNodeIncDecExpressionBuilder.cpp
index 5dd0f4e37ab19f852e05d222b703b7cac0eaab0f..2b64fcd03c2aeedb6ab3902cf374791205344815 100644
--- a/tests/test_ASTNodeIncDecExpressionBuilder.cpp
+++ b/tests/test_ASTNodeIncDecExpressionBuilder.cpp
@@ -8,6 +8,7 @@
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <language/utils/ASTPrinter.hpp>
+#include <language/utils/OperatorRepository.hpp>
 #include <utils/Demangle.hpp>
 
 #include <pegtl/string_input.hpp>
@@ -299,6 +300,13 @@ x--;
 
   SECTION("Errors")
   {
+    SECTION("Undefined operator")
+    {
+      auto& operator_repository = OperatorRepository::instance();
+      auto optional_value_type  = operator_repository.getIncDecOperatorValueType("string ++");
+      REQUIRE(not optional_value_type.has_value());
+    }
+
     SECTION("Invalid operand type")
     {
       auto ast = std::make_unique<ASTNode>();
diff --git a/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp b/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp
index 96290c4499e9c3de205a6c0a3769644e6cea2525..2ebe22cab68dc735836054b58ca4382340e09030 100644
--- a/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp
+++ b/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp
@@ -202,10 +202,16 @@ not b;
 
     SECTION("errors")
     {
-      CHECK_AST_THROWS_WITH(R"(let n:N; not n;)", "invalid implicit conversion: N -> B");
-      CHECK_AST_THROWS_WITH(R"(not 2;)", "invalid implicit conversion: Z -> B");
-      CHECK_AST_THROWS_WITH(R"(not -2.3;)", "invalid implicit conversion: R -> B");
-      CHECK_AST_THROWS_WITH(R"(not "foo";)", "invalid implicit conversion: string -> B");
+      auto error_message = [](std::string type_name) {
+        return std::string{R"(undefined unary operator
+note: unexpected operand type )"} +
+               type_name;
+      };
+
+      CHECK_AST_THROWS_WITH(R"(let n:N; not n;)", error_message("N"));
+      CHECK_AST_THROWS_WITH(R"(not 2;)", error_message("Z"));
+      CHECK_AST_THROWS_WITH(R"(not -2.3;)", error_message("R"));
+      CHECK_AST_THROWS_WITH(R"(not "foo";)", error_message("string"));
     }
 
     SECTION("Invalid value type for unary not")
diff --git a/tests/test_UnaryExpressionProcessor.cpp b/tests/test_UnaryExpressionProcessor.cpp
index e933902adaefd968cc3036c8bbf007b29d054f39..b2cec8c797a48457b8d19c112c7490e9e275247a 100644
--- a/tests/test_UnaryExpressionProcessor.cpp
+++ b/tests/test_UnaryExpressionProcessor.cpp
@@ -70,10 +70,16 @@ TEST_CASE("UnaryExpressionProcessor", "[language]")
     {
       SECTION("bad implicit conversions")
       {
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", "invalid implicit conversion: N -> B");
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", "invalid implicit conversion: Z -> B");
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", "invalid implicit conversion: R -> B");
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", "invalid implicit conversion: string -> B");
+        auto error_message = [](std::string type_name) {
+          return std::string{R"(undefined unary operator
+note: unexpected operand type )"} +
+                 type_name;
+        };
+
+        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N"));
+        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z"));
+        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R"));
+        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string"));
       }
     }
   }