How to change WooCommerce loop product title HTML output in single product page and archive page

The main problems with what you’ve got are tha

  1. your close bracket } for the is_product() case is in the wrong place: it should be before the elseif not at the bottom of the block. (This is what Tom meant by indenting your code correctly: if you’d indented each {…}, which your IDE will do for you, this would have been obvious.)
  2. this code, defining the functions, will run before WordPress has enough state loaded to determine is_product() or is_shop(): at the point the theme is loaded the main query hasn’t been initialised yet.

It is OK to wrap function definitions in if-else, e.g. see WordPress’s pluggable.php or indeed wc-template-functions which is what you’re overriding here, but that’s not the correct approach for this. Instead, test is_product and is_shop inside the function:

function woocommerce_template_loop_product_title() {
    if ( is_product() ){
        echo '<h2 class="product-title">' . get_the_title() . '</h2>';
    } elseif ( is_shop() ) {
        echo '<span class="product-title">' . get_the_title() . '</span>';
    }
    // do you need an 'else' case here too?
}

because by the point this function is called WordPress will have loaded enough page state for is_product and is_shop.