Yes, `the_post_navigation()` works outside of the loop on single post views. 

Following the function calls, [`the_post_navigation()`](https://developer.wordpress.org/reference/functions/the_post_navigation/) uses [`get_the_post_navigation()`](https://developer.wordpress.org/reference/functions/get_the_post_navigation/) which uses `get_previous_post_link()` and `get_next_post_link()` which use [`get_adjacent_post_link()`](https://developer.wordpress.org/reference/functions/get_adjacent_post_link/) which finally uses [`get_post()`](https://developer.wordpress.org/reference/functions/get_post/) which defaults to the global `$post` object.

Here's an excerpt of `get_adjacent_post_link()`:

    function get_adjacent_post_link( $format, $link, $in_same_term = false,
        $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {

        if ( $previous && is_attachment() )
            $post = get_post( get_post()->post_parent );
        else
            $post = get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
     
        if ( ! $post ) {
            $output = '';
        } else {
            $title = $post->post_title;
        ...