Gutenberg: How filter blocks of a certain type with parse_blocks recursively?

parse_blocks() simply returns all blocks found in the post content, so for what you’re trying to get, you could use a function which calls itself recursively like so:

function my_find_heading_blocks( $blocks ) {
    $list = array();

    foreach ( $blocks as $block ) {
        if ( 'core/heading' === $block['blockName'] ) {
            // add current item, if it's a heading block
            $list[] = $block;
        } elseif ( ! empty( $block['innerBlocks'] ) ) {
            // or call the function recursively, to find heading blocks in inner blocks
            $list = array_merge( $list, my_find_heading_blocks( $block['innerBlocks'] ) );
        }
    }

    return $list;
}

// Sample usage:
$post = get_post();
$blocks = parse_blocks( $post->post_content );
$headings = my_find_heading_blocks( $blocks );