diff --git a/tree/dataframe/inc/ROOT/RDFInterface.hxx b/tree/dataframe/inc/ROOT/RDFInterface.hxx
index 93492e2c9a61d18d621ddf52f0a58048cfedb029..0c31fdf64db19f661fab03274de7f93b071ac483 100644
--- a/tree/dataframe/inc/ROOT/RDFInterface.hxx
+++ b/tree/dataframe/inc/ROOT/RDFInterface.hxx
@@ -219,10 +219,10 @@ public:
       using BaseNodeType_t = typename std::remove_pointer<decltype(upcastNodeOnHeap)>::type::element_type;
       RInterface<BaseNodeType_t> upcastInterface(*upcastNodeOnHeap, fImplWeakPtr, fCustomColumns, fBranchNames,
                                                  fDataSource);
-      const auto prevNodeTypeName = upcastInterface.GetNodeTypeName();
       const auto jittedFilter = std::make_shared<RDFDetail::RJittedFilter>(df.get(), name);
-      RDFInternal::BookFilterJit(jittedFilter.get(), upcastNodeOnHeap, prevNodeTypeName, name, expression, aliasMap,
-                                 branches, fCustomColumns, tree, fDataSource, df->GetID());
+
+      RDFInternal::BookFilterJit(jittedFilter.get(), upcastNodeOnHeap, "ROOT::Detail::RDF::RNode", name, expression,
+                                 aliasMap, branches, fCustomColumns, tree, fDataSource, df->GetID());
 
       df->Book(jittedFilter.get());
       return RInterface<RDFDetail::RJittedFilter, DS_t>(std::move(jittedFilter), fImplWeakPtr, fCustomColumns,
@@ -423,12 +423,12 @@ public:
          fProxiedPtr, fImplWeakPtr, fCustomColumns, fBranchNames, fDataSource);
 
       // build a string equivalent to
-      // "(RInterface<nodetype*>*)(this)->Snapshot<Ts...>(treename,filename,*(ColumnNames_t*)(&columnList), options)"
+      // "resPtr = (RInterface<nodetype*>*)(this)->Snapshot<Ts...>(args...)"
       RResultPtr<RInterface<RLoopManager>> resPtr;
       snapCall << "*reinterpret_cast<ROOT::RDF::RResultPtr<ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager>>*>("
-               << RDFInternal::PrettyPrintAddr(&resPtr) << ") = reinterpret_cast<ROOT::RDF::RInterface<"
-               << upcastInterface.GetNodeTypeName() << ">*>(" << RDFInternal::PrettyPrintAddr(&upcastInterface)
-               << ")->Snapshot<";
+               << RDFInternal::PrettyPrintAddr(&resPtr)
+               << ") = reinterpret_cast<ROOT::RDF::RInterface<ROOT::Detail::RDF::RNode>*>("
+               << RDFInternal::PrettyPrintAddr(&upcastInterface) << ")->Snapshot<";
 
       const auto &customCols = fCustomColumns.GetNames();
       const auto dontConvertVector = false;
@@ -538,9 +538,9 @@ public:
       // "(RInterface<nodetype*>*)(this)->Cache<Ts...>(*(ColumnNames_t*)(&columnList))"
       RInterface<RLoopManager> resRDF(std::make_shared<ROOT::Detail::RDF::RLoopManager>(0));
       cacheCall << "*reinterpret_cast<ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager>*>("
-                << RDFInternal::PrettyPrintAddr(&resRDF) << ") = reinterpret_cast<ROOT::RDF::RInterface<"
-                << upcastInterface.GetNodeTypeName() << ">*>(" << RDFInternal::PrettyPrintAddr(&upcastInterface)
-                << ")->Cache<";
+                << RDFInternal::PrettyPrintAddr(&resRDF)
+                << ") = reinterpret_cast<ROOT::RDF::RInterface<ROOT::Detail::RDF::RNode>*>("
+                << RDFInternal::PrettyPrintAddr(&upcastInterface) << ")->Cache<";
 
       const auto &customCols = fCustomColumns.GetNames();
       for (auto &c : columnList) {
@@ -1756,11 +1756,6 @@ private:
       }
    }
 
-   /// Return string containing fully qualified type name of the node pointed by fProxied.
-   /// The method is only defined for RInterface<{RFilterBase,RCustomColumnBase,RRangeBase,RLoopManager}> as it should
-   /// only be called on "upcast" RInterfaces.
-   inline static std::string GetNodeTypeName();
-
    // Type was specified by the user, no need to infer it
    template <typename ActionTag, typename... BranchTypes, typename ActionResultType,
              typename std::enable_if<!RDFInternal::TNeedJitting<BranchTypes...>::value, int>::type = 0>
@@ -1806,10 +1801,10 @@ private:
                                                  fDataSource);
 
       auto jittedActionOnHeap = RDFInternal::MakeSharedOnHeap(std::make_shared<RDFInternal::RJittedAction>(*lm));
-      auto toJit =
-         RDFInternal::JitBuildAction(validColumnNames, upcastInterface.GetNodeTypeName(), upcastNodeOnHeap,
-                                     typeid(std::shared_ptr<ActionResultType>), typeid(ActionTag), rOnHeap, tree,
-                                     nSlots, fCustomColumns, fDataSource, jittedActionOnHeap, lm->GetID());
+
+      auto toJit = RDFInternal::JitBuildAction(
+         validColumnNames, "ROOT::Detail::RDF::RNode", upcastNodeOnHeap, typeid(std::shared_ptr<ActionResultType>),
+         typeid(ActionTag), rOnHeap, tree, nSlots, fCustomColumns, fDataSource, jittedActionOnHeap, lm->GetID());
       lm->Book(jittedActionOnHeap->get());
       lm->ToJit(toJit);
       return MakeResultPtr(r, lm, *jittedActionOnHeap);
@@ -2018,31 +2013,8 @@ protected:
    }
 };
 
-template <>
-inline std::string RInterface<RDFDetail::RFilterBase>::GetNodeTypeName()
-{
-   return "ROOT::Detail::RDF::RFilterBase";
-}
-
-template <>
-inline std::string RInterface<RDFDetail::RLoopManager>::GetNodeTypeName()
-{
-   return "ROOT::Detail::RDF::RLoopManager";
-}
-
-template <>
-inline std::string RInterface<RDFDetail::RRangeBase>::GetNodeTypeName()
-{
-   return "ROOT::Detail::RDF::RRangeBase";
-}
-
-template <>
-inline std::string RInterface<RDFDetail::RJittedFilter>::GetNodeTypeName()
-{
-   return "ROOT::Detail::RDF::RJittedFilter";
-}
+} // end NS RDF
 
-} // namespace RDF
 
 } // namespace ROOT
 
diff --git a/tree/dataframe/inc/ROOT/RDFInterfaceUtils.hxx b/tree/dataframe/inc/ROOT/RDFInterfaceUtils.hxx
index 59c1d2a42133208050565231bffe4d801b652a08..44debabb3814c9d0e234bd3fd815ca76331407de 100644
--- a/tree/dataframe/inc/ROOT/RDFInterfaceUtils.hxx
+++ b/tree/dataframe/inc/ROOT/RDFInterfaceUtils.hxx
@@ -266,13 +266,9 @@ std::shared_ptr<T> *MakeSharedOnHeap(const std::shared_ptr<T> &shPtr)
 
 bool AtLeastOneEmptyString(const std::vector<std::string_view> strings);
 
-/* The following functions upcast shared ptrs to RFilter, RCustomColumn, RRange to their parent class (***Base).
- * Shared ptrs to RLoopManager are just copied, as well as shared ptrs to ***Base classes. */
-std::shared_ptr<RFilterBase> UpcastNode(const std::shared_ptr<RFilterBase> ptr);
-std::shared_ptr<RCustomColumnBase> UpcastNode(const std::shared_ptr<RCustomColumnBase> ptr);
-std::shared_ptr<RRangeBase> UpcastNode(const std::shared_ptr<RRangeBase> ptr);
-std::shared_ptr<RLoopManager> UpcastNode(const std::shared_ptr<RLoopManager> ptr);
-std::shared_ptr<RJittedFilter> UpcastNode(const std::shared_ptr<RJittedFilter> ptr);
+/// Take a shared_ptr<AnyNodeType> and return a shared_ptr<RNode>.
+/// This works for RLoopManager nodes as well as filters and ranges.
+std::shared_ptr<RNode> UpcastNode(std::shared_ptr<RNode> ptr);
 
 ColumnNames_t GetValidatedColumnNames(RLoopManager &lm, const unsigned int nColumns, const ColumnNames_t &columns,
                                       const ColumnNames_t &datasetColumns, const ColumnNames_t &validCustomColumns,
diff --git a/tree/dataframe/src/RDFInterfaceUtils.cxx b/tree/dataframe/src/RDFInterfaceUtils.cxx
index 7ff315247a879f8de94d4736b5497c5f8ef6298b..38158d438321119795838bb77c3fe3b278aa7af8 100644
--- a/tree/dataframe/src/RDFInterfaceUtils.cxx
+++ b/tree/dataframe/src/RDFInterfaceUtils.cxx
@@ -754,32 +754,7 @@ bool AtLeastOneEmptyString(const std::vector<std::string_view> strings)
    return false;
 }
 
-/*** Take a shared_ptr<Node<T1,T2,...>> and return a shared_ptr<NodeBase> ***/
-std::shared_ptr<RFilterBase> UpcastNode(const std::shared_ptr<RFilterBase> ptr)
-{
-   return ptr;
-}
-
-std::shared_ptr<RCustomColumnBase> UpcastNode(const std::shared_ptr<RCustomColumnBase> ptr)
-{
-   return ptr;
-}
-
-std::shared_ptr<RRangeBase> UpcastNode(const std::shared_ptr<RRangeBase> ptr)
-{
-   return ptr;
-}
-
-std::shared_ptr<RLoopManager> UpcastNode(const std::shared_ptr<RLoopManager> ptr)
-{
-   return ptr;
-}
-
-std::shared_ptr<RJittedFilter> UpcastNode(const std::shared_ptr<RJittedFilter> ptr)
-{
-   return ptr;
-}
-/****************************************************************************/
+std::shared_ptr<RNode> UpcastNode(std::shared_ptr<RNode> ptr) { return ptr; }
 
 /// Given the desired number of columns and the user-provided list of columns:
 /// * fallback to using the first nColumns default columns if needed (or throw if nColumns > nDefaultColumns)