Is_single() conditional tag returns null in query

is_single() and is_page() refer to the main query and as far as I know are not altered to match the individual posts in a Loop. They refer to the type of page not to the individual posts. Try and you should see what I mean:

$data = array();
$args = array(
    'posts_per_page' => '-1',
    'post_type' => array('post', 'page'),
    'orderby' => 'menu_order',
    'order' => 'ASC',
);

$loop = new WP_Query($args);
if( $loop->have_posts() ):
    while( $loop->have_posts() ): $loop->the_post();
        $id = $post->ID;        
        the_title(); var_dump($post->post_type,is_single(),is_page(),$loop->is_single(),$loop->is_page());
        echo '<br>';
    endwhile;
endif;
wp_reset_postdata();

The simple solution is just to check the post type itself

if('post' == $post->post_type) {
    $type="article";
} elseif('page' == $post->post_type) {
    $type="page";
}

Also this,

$id = $loop->post->ID;   

is overly complicated. $post->ID is the same value. Or use get_theID()