Ordering Custom Post Types with WP_Query

Here’s a plugin that uses the posts_orderby filter to support a custom _post_type__in orderby in WP_Query that orders the post types by their array placements:

<?php
/**
 *  Plugin Name: Support for _post_type__in orderby in WP_Query
 *  Plugin URI:  https://wordpress.stackexchange.com/a/392910/26350
 */
add_filter ( 'posts_orderby', function ( $orderby, \WP_Query $q ) use ( $wpdb ) {
    // Do nothing.
    if ( '_post_type__in' !== $q->get( 'orderby' ) ) {
        return $orderby;
    }

    // Custom _post_type__in ordering using FIELD on post types array items.
    $post_type = $q->get( 'post_type' );
    
    if ( ! empty( $post_type ) && is_array( $post_type ) ) {
        $post_type__in        = array_map( 'sanitize_title_for_query', $post_type );
        $post_type__in_string = "'" . implode( "','", $post_type__in ) . "'";
        return $orderby       = "FIELD( {$wpdb->posts}.post_type," . $post_type__in_string . ' )';
    }

    return $orderby;
}, 10, 2 );

Usage example:

$args = array(
    'post_type' => array( 'expressions', 'videos', 'lingo', 'exercises' ),
    'orderby'   => '_post_type__in',
);

$query = new WP_Query( $args );

ps: we use orderby post_name__in in core for guidance here. You can then look into expanding this to support multiple orderby fields.