diff --git src/wp-includes/class-wp-post-type-labels.php src/wp-includes/class-wp-post-type-labels.php
new file mode 100644
index 0000000..898ca12
--- /dev/null
+++ src/wp-includes/class-wp-post-type-labels.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Post API: WP_Post_Type_Labels class
+ *
+ * @package WordPress
+ * @subpackage Post
+ * @since 4.7.0
+ */
+
+/**
+ * Core class used for providing post type labels.
+ *
+ * @since 4.7.0
+ *
+ * @see register_post_type()
+ * @see get_post_type_labels()
+ *
+ * @property string name
+ * @property string singular_name
+ * @property string add_new
+ * @property string add_new_item
+ * @property string edit_item
+ * @property string new_item
+ * @property string view_item
+ * @property string view_items
+ * @property string search_items
+ * @property string not_found
+ * @property string not_found_in_trash
+ * @property string parent_item_colon
+ * @property string all_items
+ * @property string archives
+ * @property string insert_into_item
+ * @property string uploaded_to_this_item
+ * @property string featured_image
+ * @property string set_featured_image
+ * @property string remove_featured_image
+ * @property string use_featured_image
+ * @property string filter_items_list
+ * @property string items_list_navigation
+ * @property string items_list
+ * @property string menu_name
+ * @property string name_admin_bar
+ */
+final class WP_Post_Type_Labels {
+	/**
+	 * Current post type.
+	 *
+	 * @since 4.7.0
+	 * @access protected
+	 * @var WP_Post_Type
+	 */
+	protected $post_type;
+
+	/**
+	 * The default labels of the post type.
+	 *
+	 * @since 4.7.0
+	 * @access protected
+	 * @var array
+	 */
+	protected $defaults = array();
+
+	/**
+	 * Constructor.
+	 *
+	 * Will populate object properties from the provided arguments and assign other
+	 * default properties based on that information.
+	 *
+	 * @since  4.7.0
+	 * @access public
+	 *
+	 * @see register_post_type()
+	 *
+	 * @param WP_Post_Type $post_type Post type object.
+	 */
+	public function __construct( $post_type ) {
+		$this->post_type = $post_type;
+
+		$this->defaults = (array) $post_type->labels;
+	}
+
+	/**
+	 * Returns the (non-)hierarchical default labels.
+	 *
+	 * @since 4.7.0
+	 * @access protected
+	 *
+	 * @return array Hierarchical vs non-hierarchical default labels.
+	 */
+	protected function nohier_vs_hier_defaults() {
+		$nohier_vs_hier_defaults = array(
+			'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
+			'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
+			'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
+			'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
+			'edit_item' => array( __('Edit Post'), __('Edit Page') ),
+			'new_item' => array( __('New Post'), __('New Page') ),
+			'view_item' => array( __('View Post'), __('View Page') ),
+			'view_items' => array( __('View Posts'), __('View Pages') ),
+			'search_items' => array( __('Search Posts'), __('Search Pages') ),
+			'not_found' => array( __('No posts found.'), __('No pages found.') ),
+			'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
+			'parent_item_colon' => array( null, __('Parent Page:') ),
+			'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) ),
+			'archives' => array( __( 'Post Archives' ), __( 'Page Archives' ) ),
+			'insert_into_item' => array( __( 'Insert into post' ), __( 'Insert into page' ) ),
+			'uploaded_to_this_item' => array( __( 'Uploaded to this post' ), __( 'Uploaded to this page' ) ),
+			'featured_image' => array( __( 'Featured Image' ), __( 'Featured Image' ) ),
+			'set_featured_image' => array( __( 'Set featured image' ), __( 'Set featured image' ) ),
+			'remove_featured_image' => array( __( 'Remove featured image' ), __( 'Remove featured image' ) ),
+			'use_featured_image' => array( __( 'Use as featured image' ), __( 'Use as featured image' ) ),
+			'filter_items_list' => array( __( 'Filter posts list' ), __( 'Filter pages list' ) ),
+			'items_list_navigation' => array( __( 'Posts list navigation' ), __( 'Pages list navigation' ) ),
+			'items_list' => array( __( 'Posts list' ), __( 'Pages list' ) ),
+		);
+
+		return $nohier_vs_hier_defaults;
+	}
+
+	/**
+	 * Magic method for checking the existence of a certain post type label.
+	 *
+	 * @since 4.7.0
+	 * @access public
+	 *
+	 * @param string $key Label to check if set.
+	 * @return bool Whether the given label is set.
+	 */
+	public function __isset( $key ) {
+		return null !== $this->{$key};
+	}
+
+	/**
+	 * Magic method for accessing post type labels.
+	 *
+	 * @since 4.7.0
+	 * @access public
+	 *
+	 * @param string $key Label to retrieve.
+	 * @return string THe post type label.
+	 */
+	public function __get( $key ) {
+		/**
+		 * Filters the labels of a specific post type.
+		 *
+		 * The dynamic portion of the hook name, `$post_type`, refers to
+		 * the post type slug.
+		 *
+		 * @since 3.5.0
+		 *
+		 * @see get_post_type_labels() for the full list of labels.
+		 *
+		 * @param object $labels Object with labels for the post type as member variables.
+		 */
+		$original = apply_filters( "post_type_labels_{$this->post_type->name}", $this->defaults );
+
+		$defaults = array();
+
+		foreach ( $this->nohier_vs_hier_defaults() as $default_key => $value ) {
+			$defaults[ $default_key ] = $this->post_type->hierarchical ? $value[1] : $value[0];
+		}
+
+		$labels = array_merge( $defaults, $original );
+
+		if ( isset( $labels[ $key ] ) ) {
+			return $labels[ $key ];
+		}
+
+		if ( 'name' === $key && isset( $this->post_type->label ) ) {
+			return $this->post_type->label;
+		}
+
+		if ( 'singular_name' === $key ) {
+			$key = 'name';
+		}
+
+		if ( 'name_admin_bar' === $key ) {
+			return isset( $this->singular_name ) ? $this->singular_name : $this->post_type->name;
+		}
+
+		if ( 'menu_name' === $key ) {
+			$key = 'name';
+		}
+
+		if ( 'all_items' === $key ) {
+			$key = 'menu_name';
+		}
+
+		if ( 'archives' === $key ) {
+			$key = 'all_items';
+		}
+
+		return $labels[ $key ];
+	}
+}
diff --git src/wp-includes/class-wp-post-type.php src/wp-includes/class-wp-post-type.php
index 0b1d3c0..0dd44d4 100644
--- src/wp-includes/class-wp-post-type.php
+++ src/wp-includes/class-wp-post-type.php
@@ -43,7 +43,7 @@ final class WP_Post_Type {
 	 *
 	 * @since 4.6.0
 	 * @access public
-	 * @var object $labels
+	 * @var WP_Post_Type_Labels $labels
 	 */
 	public $labels;
 
diff --git src/wp-includes/post.php src/wp-includes/post.php
index c02108e..01a7ec7 100644
--- src/wp-includes/post.php
+++ src/wp-includes/post.php
@@ -1273,60 +1273,10 @@ function _post_type_meta_capabilities( $capabilities = null ) {
  * @access private
  *
  * @param object|WP_Post_Type $post_type_object Post type object.
- * @return object Object with all the labels as member variables.
+ * @return WP_Post_Type_Labels Object with all the labels as member variables.
  */
 function get_post_type_labels( $post_type_object ) {
-	$nohier_vs_hier_defaults = array(
-		'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
-		'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
-		'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
-		'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
-		'edit_item' => array( __('Edit Post'), __('Edit Page') ),
-		'new_item' => array( __('New Post'), __('New Page') ),
-		'view_item' => array( __('View Post'), __('View Page') ),
-		'view_items' => array( __('View Posts'), __('View Pages') ),
-		'search_items' => array( __('Search Posts'), __('Search Pages') ),
-		'not_found' => array( __('No posts found.'), __('No pages found.') ),
-		'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
-		'parent_item_colon' => array( null, __('Parent Page:') ),
-		'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) ),
-		'archives' => array( __( 'Post Archives' ), __( 'Page Archives' ) ),
-		'insert_into_item' => array( __( 'Insert into post' ), __( 'Insert into page' ) ),
-		'uploaded_to_this_item' => array( __( 'Uploaded to this post' ), __( 'Uploaded to this page' ) ),
-		'featured_image' => array( __( 'Featured Image' ), __( 'Featured Image' ) ),
-		'set_featured_image' => array( __( 'Set featured image' ), __( 'Set featured image' ) ),
-		'remove_featured_image' => array( __( 'Remove featured image' ), __( 'Remove featured image' ) ),
-		'use_featured_image' => array( __( 'Use as featured image' ), __( 'Use as featured image' ) ),
-		'filter_items_list' => array( __( 'Filter posts list' ), __( 'Filter pages list' ) ),
-		'items_list_navigation' => array( __( 'Posts list navigation' ), __( 'Pages list navigation' ) ),
-		'items_list' => array( __( 'Posts list' ), __( 'Pages list' ) ),
-	);
-	$nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
-
-	$labels = _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
-
-	$post_type = $post_type_object->name;
-
-	$default_labels = clone $labels;
-
-	/**
-	 * Filters the labels of a specific post type.
-	 *
-	 * The dynamic portion of the hook name, `$post_type`, refers to
-	 * the post type slug.
-	 *
-	 * @since 3.5.0
-	 *
-	 * @see get_post_type_labels() for the full list of labels.
-	 *
-	 * @param object $labels Object with labels for the post type as member variables.
-	 */
-	$labels = apply_filters( "post_type_labels_{$post_type}", $labels );
-
-	// Ensure that the filtered labels contain all required default values.
-	$labels = (object) array_merge( (array) $default_labels, (array) $labels );
-
-	return $labels;
+	return new WP_Post_Type_Labels( $post_type_object );
 }
 
 /**
diff --git src/wp-settings.php src/wp-settings.php
index 80f556c..cd4b37a 100644
--- src/wp-settings.php
+++ src/wp-settings.php
@@ -163,6 +163,7 @@ require( ABSPATH . WPINC . '/post.php' );
 require( ABSPATH . WPINC . '/class-walker-page.php' );
 require( ABSPATH . WPINC . '/class-walker-page-dropdown.php' );
 require( ABSPATH . WPINC . '/class-wp-post-type.php' );
+require( ABSPATH . WPINC . '/class-wp-post-type-labels.php' );
 require( ABSPATH . WPINC . '/class-wp-post.php' );
 require( ABSPATH . WPINC . '/post-template.php' );
 require( ABSPATH . WPINC . '/revision.php' );
diff --git tests/phpunit/tests/post/getPostTypeLabels.php tests/phpunit/tests/post/getPostTypeLabels.php
new file mode 100644
index 0000000..59b9126
--- /dev/null
+++ tests/phpunit/tests/post/getPostTypeLabels.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @group post
+ */
+class Tests_Get_Post_Type_Labels extends WP_UnitTestCase {
+	public function test_returns_an_object() {
+		$this->assertInternalType( 'object', get_post_type_labels( (object) array(
+			'name'         => 'foo',
+			'labels'       => array(),
+			'hierarchical' => false,
+		) ) );
+	}
+
+	public function test_returns_hierachical_labels() {
+		$labels = get_post_type_labels( (object) array(
+			'name'         => 'foo',
+			'labels'       => array(),
+			'hierarchical' => true,
+		) );
+
+		$this->assertSame( 'Pages', $labels->name );
+	}
+
+	public function test_existing_labels_are_not_overridden() {
+		$labels = get_post_type_labels( (object) array(
+			'name'         => 'foo',
+			'labels'       => array(
+				'singular_name' => 'Foo',
+			),
+			'hierarchical' => false,
+		) );
+
+		$this->assertSame( 'Foo', $labels->singular_name );
+	}
+
+	public function test_name_admin_bar_label_should_fall_back_to_singular_name() {
+		$labels = get_post_type_labels( (object) array(
+			'name'         => 'foo',
+			'labels'       => array(
+				'singular_name' => 'Foo',
+			),
+			'hierarchical' => false,
+		) );
+
+		$this->assertSame( 'Foo', $labels->name_admin_bar );
+	}
+
+	public function test_menu_name_should_fall_back_to_name() {
+		$labels = get_post_type_labels( (object) array(
+			'name'         => 'foo',
+			'labels'       => array(
+				'name' => 'Bar',
+			),
+			'hierarchical' => false,
+		) );
+
+		$this->assertSame( 'Bar', $labels->menu_name );
+	}
+
+	public function test_labels_should_be_added_when_registering_a_post_type() {
+		$post_type_object = register_post_type( 'foo', array(
+			'labels' => array(
+				'singular_name' => 'bar',
+			),
+		) );
+
+		unregister_post_type( 'foo' );
+
+		$this->assertObjectHasAttribute( 'labels', $post_type_object );
+		$this->assertObjectHasAttribute( 'label', $post_type_object );
+		$this->assertNotNull( $post_type_object->labels->not_found_in_trash );
+	}
+
+	public function test_label_should_be_derived_from_labels_when_registering_a_post_type() {
+		$post_type_object = register_post_type( 'foo', array(
+			'labels' => array(
+				'name' => 'bar',
+			),
+		) );
+
+		$this->assertSame( 'bar', $post_type_object->label );
+
+		unregister_post_type( 'foo' );
+	}
+
+	/**
+	 * @ticket 33543
+	 */
+	public function test_should_fall_back_on_defaults_when_filtered_labels_do_not_contain_the_keys() {
+		add_filter( 'post_type_labels_foo', array( $this, 'filter_post_type_labels' ) );
+		register_post_type( 'foo' );
+
+		$this->assertNotNull( get_post_type_object( 'foo' )->labels->featured_image );
+		$this->assertNotNull( get_post_type_object( 'foo' )->labels->set_featured_image );
+
+		unregister_post_type( 'foo' );
+		remove_filter( 'post_type_labels_foo', array( $this, 'filter_post_type_labels' ) );
+	}
+
+	public function filter_post_type_labels( $labels ) {
+		unset( $labels->featured_image );
+		unset( $labels->set_featured_image );
+
+		return $labels;
+	}
+}
diff --git tests/phpunit/tests/post/types.php tests/phpunit/tests/post/types.php
index e7dcf84..31a51c2 100644
--- tests/phpunit/tests/post/types.php
+++ tests/phpunit/tests/post/types.php
@@ -220,27 +220,6 @@ class Tests_Post_Types extends WP_UnitTestCase {
 	}
 
 	/**
-	 * @ticket 33543
-	 */
-	function test_get_post_type_labels_should_fall_back_on_defaults_when_filtered_labels_do_not_contain_the_keys() {
-		add_filter( 'post_type_labels_foo', array( $this, 'filter_post_type_labels' ) );
-		register_post_type( 'foo' );
-
-		$this->assertObjectHasAttribute( 'featured_image', get_post_type_object( 'foo' )->labels );
-		$this->assertObjectHasAttribute( 'set_featured_image', get_post_type_object( 'foo' )->labels );
-
-		_unregister_post_type( 'foo' );
-		remove_filter( 'post_type_labels_foo', array( $this, 'filter_post_type_labels' ) );
-	}
-
-	public function filter_post_type_labels( $labels ) {
-		unset( $labels->featured_image );
-		unset( $labels->set_featured_image );
-		return $labels;
-	}
-
-
-	/**
 	 * @ticket 30013
 	 */
 	public function test_get_post_type_object_with_non_scalar_values() {
