From the SystemVerilog perspective, the code is perfectly legal and would not generate a compiler error. One of the pitfalls of using macros is that you do not see the underlying code generated by the macro. I recommend learning the UVM without using any macros and introduce them later as you begin to understand what the macros are doing. See my blog post and this cookbook article to get a better understanding.
The reason B::get_type() does not generate a compiler error is because the `uvm_object_utils(A) macro declares A::get_Type for you, and since class B is an extension of class A, B inherits get_type() from A. So both of your arguments to set_type_override() call the same function. I’ts not until sumulation run time that the UVM checks that the two functions return a handle to the same wrapper object.
IMHO, it would be better to make these kinds of messages a UVM_ERROR by default. You can do that with the following statement:
uvm_top.set_report_severity_id_override(UVM_WARNING,"TYPDUP",UVM_ERROR);