diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php
index 256076f..171c972 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -108,7 +108,13 @@ public function setPropertyValues($values) {
   }
 
   /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
+   * Gets the definition of a contained property.
+   *
+   * @param string $name
+   *   The name of property.
+   *
+   * @return array|FALSE
+   *   The definition of the property or FALSE if the property does not exist.
    */
   public function getPropertyDefinition($name) {
     if (isset($this->definition['mapping'][$name])) {
@@ -120,7 +126,11 @@ public function getPropertyDefinition($name) {
   }
 
   /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * Gets an array of property definitions of contained properties.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *   An array of property definitions of contained properties, keyed by
+   *   property name.
    */
   public function getPropertyDefinitions() {
     $list = array();
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 1572129..ee5ac66 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -9,9 +9,9 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
+use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
@@ -66,7 +66,7 @@
   /**
    * Local cache for field definitions.
    *
-   * @see ContentEntityBase::getPropertyDefinitions()
+   * @see ContentEntityBase::getFieldDefinitions()
    *
    * @var array
    */
@@ -129,6 +129,25 @@
   protected $isDefaultRevision = TRUE;
 
   /**
+   * {@inheritdoc}
+   */
+  public static function createDataDefinition($data_type) {
+    $parts = explode(':', $data_type);
+    if ($parts[0] != 'entity') {
+      throw new \InvalidArgumentException('Data type must be in the form of "entity:ENTITY_TYPE:BUNDLE."');
+    }
+    $definition = EntityDataDefinition::create();
+    // Set the passed entity type and bundle.
+    if (isset($parts[1])) {
+      $definition->setEntityTypeId($parts[1]);
+    }
+    if (isset($parts[2])) {
+      $definition->setBundles(array($parts[2]));
+    }
+    return $definition;
+  }
+
+  /**
    * Overrides Entity::__construct().
    */
   public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = array()) {
@@ -212,15 +231,11 @@ public function preSaveRevision(EntityStorageControllerInterface $storage_contro
    * {@inheritdoc}
    */
   public function getDefinition() {
-    // @todo: This does not make much sense, so remove once TypedDataInterface
-    // is removed. See https://drupal.org/node/2002138.
+    $definition = EntityDataDefinition::create($this->getEntityTypeId());
     if ($this->bundle() != $this->getEntityTypeId()) {
-      $type = 'entity:' . $this->getEntityTypeId() . ':' . $this->bundle();
+      $definition->setBundles(array($this->bundle()));
     }
-    else {
-      $type = 'entity:' . $this->getEntityTypeId();
-    }
-    return DataDefinition::create($type);
+    return $definition;
   }
 
   /**
@@ -378,7 +393,7 @@ public function uuid() {
    * {@inheritdoc}
    */
   public function hasField($field_name) {
-    return (bool) $this->getPropertyDefinition($field_name);
+    return (bool) $this->getFieldDefinition($field_name);
   }
 
   /**
@@ -404,7 +419,7 @@ protected function getTranslatedField($name, $langcode) {
     // Populate $this->fields to speed-up further look-ups and to keep track of
     // fields objects, possibly holding changes to field values.
     if (!isset($this->fields[$name][$langcode])) {
-      $definition = $this->getPropertyDefinition($name);
+      $definition = $this->getFieldDefinition($name);
       if (!$definition) {
         throw new \InvalidArgumentException('Field ' . String::checkPlain($name) . ' is unknown.');
       }
@@ -454,7 +469,7 @@ public function set($name, $value, $notify = TRUE) {
    */
   public function getProperties($include_computed = FALSE) {
     $properties = array();
-    foreach ($this->getPropertyDefinitions() as $name => $definition) {
+    foreach ($this->getFieldDefinitions() as $name => $definition) {
       if ($include_computed || !$definition->isComputed()) {
         $properties[$name] = $this->get($name);
       }
@@ -472,9 +487,9 @@ public function getIterator() {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinition($name) {
+  public function getFieldDefinition($name) {
     if (!isset($this->fieldDefinitions)) {
-      $this->getPropertyDefinitions();
+      $this->getFieldDefinitions();
     }
     if (isset($this->fieldDefinitions[$name])) {
       return $this->fieldDefinitions[$name];
@@ -487,7 +502,7 @@ public function getPropertyDefinition($name) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public function getFieldDefinitions() {
     if (!isset($this->fieldDefinitions)) {
       $bundle = $this->bundle != $this->entityTypeId ? $this->bundle : NULL;
       $this->fieldDefinitions = \Drupal::entityManager()->getFieldDefinitions($this->entityTypeId, $bundle);
@@ -566,7 +581,7 @@ public function language() {
    */
   protected function setDefaultLangcode() {
     // Get the language code if the property exists.
-    if ($this->getPropertyDefinition('langcode') && ($item = $this->get('langcode')) && isset($item->language)) {
+    if ($this->hasField('langcode') && ($item = $this->get('langcode')) && isset($item->language)) {
       $this->defaultLangcode = $item->language->id;
     }
     if (empty($this->defaultLangcode)) {
@@ -611,7 +626,7 @@ public function onChange($name) {
   /**
    * {@inheritdoc}
    *
-   * @return \Drupal\Core\Entity\EntityInterface
+   * @return \Drupal\Core\Entity\ContentEntityInterface
    */
   public function getTranslation($langcode) {
     // Ensure we always use the default language code when dealing with the
@@ -734,7 +749,7 @@ public function addTranslation($langcode, array $values = array()) {
 
     $this->translations[$langcode]['status'] = static::TRANSLATION_CREATED;
     $translation = $this->getTranslation($langcode);
-    $definitions = $translation->getPropertyDefinitions();
+    $definitions = $translation->getFieldDefinitions();
 
     foreach ($values as $name => $value) {
       if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) {
@@ -750,7 +765,7 @@ public function addTranslation($langcode, array $values = array()) {
    */
   public function removeTranslation($langcode) {
     if (isset($this->translations[$langcode]) && $langcode != Language::LANGCODE_DEFAULT && $langcode != $this->defaultLangcode) {
-      foreach ($this->getPropertyDefinitions() as $name => $definition) {
+      foreach ($this->getFieldDefinitions() as $name => $definition) {
         if ($definition->isTranslatable()) {
           unset($this->values[$name][$langcode]);
           unset($this->fields[$name][$langcode]);
@@ -804,7 +819,7 @@ public function updateOriginalValues() {
     if (!$this->fields) {
       return;
     }
-    foreach ($this->getPropertyDefinitions() as $name => $definition) {
+    foreach ($this->getFieldDefinitions() as $name => $definition) {
       if (!$definition->isComputed() && !empty($this->fields[$name])) {
         foreach ($this->fields[$name] as $langcode => $item) {
           $item->filterEmptyItems();
@@ -826,9 +841,9 @@ public function &__get($name) {
     if (isset($this->fields[$name][$this->activeLangcode])) {
       return $this->fields[$name][$this->activeLangcode];
     }
-    // Inline getPropertyDefinition() to speed up things.
+    // Inline getFieldDefinition() to speed up things.
     if (!isset($this->fieldDefinitions)) {
-      $this->getPropertyDefinitions();
+      $this->getFieldDefinitions();
     }
     if (isset($this->fieldDefinitions[$name])) {
       $return = $this->getTranslatedField($name, $this->activeLangcode);
@@ -930,7 +945,7 @@ public function __clone() {
     // Avoid deep-cloning when we are initializing a translation object, since
     // it will represent the same entity, only with a different active language.
     if (!$this->translationInitialize) {
-      $definitions = $this->getPropertyDefinitions();
+      $definitions = $this->getFieldDefinitions();
       foreach ($this->fields as $name => $values) {
         $this->fields[$name] = array();
         // Untranslatable fields may have multiple references for the same field
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
index cad2511..0383533 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityFormController.php
@@ -139,7 +139,8 @@ public function buildEntity(array $form, array &$form_state) {
     // edited by this form. Values of fields handled by field API are copied
     // by field_attach_extract_form_values() below.
     $values_excluding_fields = $entity_type->isFieldable() ? array_diff_key($form_state['values'], field_info_instances($entity_type_id, $entity->bundle())) : $form_state['values'];
-    $definitions = $entity->getPropertyDefinitions();
+    $definitions = $entity->getFieldDefinitions();
+
     foreach ($values_excluding_fields as $key => $value) {
       if (isset($definitions[$key])) {
         $entity->$key = $value;
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index 862b23f..59f596c 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -72,4 +72,25 @@ public static function baseFieldDefinitions($entity_type);
    */
   public function hasField($field_name);
 
+  /**
+   * Gets the definition of a contained field.
+   *
+   * @param string $name
+   *   The name of the field.
+   *
+   * @return \Drupal\Core\Field\FieldDefinitionInterface|false
+   *   The definition of the field or FALSE if the field does not exist.
+   */
+  public function getFieldDefinition($name);
+
+  /**
+   * Gets an array of field definitions of all contained fields.
+   *
+   * @return \Drupal\Core\Field\FieldDefinitionInterface[]
+   *   An array of field definitions, keyed by field name.
+   *
+   * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
+   */
+  public function getFieldDefinitions();
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 6a2385f..15cb3e1 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -374,14 +374,6 @@ public function getFieldDefinitions($entity_type_id, $bundle = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function getFieldDefinitionsByConstraints($entity_type, array $constraints) {
-    // @todo: Add support for specifying multiple bundles.
-    return $this->getFieldDefinitions($entity_type, isset($constraints['Bundle']) ? $constraints['Bundle'] : NULL);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function clearCachedFieldDefinitions() {
     unset($this->entityFieldInfo);
     unset($this->fieldDefinitions);
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 3dde8e9..1cb7ab4 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -38,7 +38,6 @@ public function getEntityTypeLabels();
    *   An array of entity field definitions, keyed by field name.
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
-   * @see \Drupal\Core\Entity\EntityManager::getFieldDefinitionsByConstraints()
    */
   public function getFieldDefinitions($entity_type_id, $bundle = NULL);
 
@@ -70,30 +69,6 @@ public function getAccessController($entity_type);
   public function getAdminRouteInfo($entity_type_id, $bundle);
 
   /**
-   * Gets an array of entity field definitions based on validation constraints.
-   *
-   * @param string $entity_type
-   *   The entity type to get field definitions for.
-   * @param array $constraints
-   *   An array of entity constraints as used for entities in typed data
-   *   definitions, i.e. an array optionally including a 'Bundle' key.
-   *   For example the constraints used by an entity reference could be:
-   *
-   * @code
-   *   array(
-   *     'Bundle' => 'article',
-   *   )
-   * @endcode
-   *
-   * @return array
-   *   An array of field definitions of entity fields, keyed by field
-   *   name.
-   *
-   * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
-   */
-  public function getFieldDefinitionsByConstraints($entity_type, array $constraints);
-
-  /**
    * Creates a new storage controller instance.
    *
    * @param string $entity_type
diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
index ca9e694..ae5b55f 100644
--- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -667,7 +667,7 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
   protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
     $record = new \stdClass();
     $values = array();
-    $definitions = $entity->getPropertyDefinitions();
+    $definitions = $entity->getFieldDefinitions();
     $schema = drupal_get_schema($this->entityType->get($table_key));
     $is_new = $entity->isNew();
 
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php
index 1d9a00f..6d2cfac 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\Core\Entity\Plugin\DataType;
+use Drupal\Core\Entity\ContentEntityBase;
 
 /**
  * Defines the base plugin for deriving data types for entity types.
@@ -22,4 +23,11 @@
  */
 abstract class Entity {
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function createDataDefinition($data_type) {
+    return ContentEntityBase::createDataDefinition($data_type);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
index 69aef4a..1b769f7 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
@@ -18,12 +18,17 @@
  *
  * The plain value of this reference is the entity object, i.e. an instance of
  * \Drupal\Core\Entity\EntityInterface. For setting the value the entity object
- * or the entity ID may be passed, whereas passing the ID is only supported if
- * an 'entity type' constraint is specified.
+ * or the entity ID may be passed.
  *
- * Some supported constraints (below the definition's 'constraints' key) are:
- *  - EntityType: The entity type. Required.
- *  - Bundle: (optional) The bundle or an array of possible bundles.
+ * Note that the definition of the referenced entity's type is required, whereas
+ * defining referencable entity bundle(s) is optional. A reference defining the
+ * type and bundle of the referenced entity can be created as following:
+ * @code
+ * $definition = \Drupal\Core\Entity\EntityDefinition::create($entity_type)
+ *   ->addConstraint('Bundle', $bundle);
+ * \Drupal\Core\TypedData\DataReferenceDefinition::create('entity')
+ *   ->setTargetDefinition($definition);
+ * @endcode
  *
  * @DataType(
  *   id = "entity_reference",
@@ -40,19 +45,13 @@ class EntityReference extends DataReferenceBase {
   protected $id;
 
   /**
-   * {@inheritdoc}
+   * Returns the definition of the referenced entity.
+   *
+   * @return \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface
+   *   The reference target's definition.
    */
   public function getTargetDefinition() {
-    $definition = array(
-      'type' => 'entity',
-    );
-    if (isset($this->definition['constraints']['EntityType'])) {
-      $definition['type'] .= ':' . $this->definition['constraints']['EntityType'];
-    }
-    if (isset($this->definition['constraints']['Bundle']) && is_string($this->definition['constraints']['Bundle'])) {
-      $definition['type'] .= ':' . $this->definition['constraints']['Bundle'];
-    }
-    return $definition;
+    return $this->definition->getTargetDefinition();
   }
 
   /**
@@ -62,7 +61,7 @@ public function getTarget() {
     if (!isset($this->target) && isset($this->id)) {
       // If we have a valid reference, return the entity object which is typed
       // data itself.
-      $this->target = entity_load($this->definition->getConstraint('EntityType'), $this->id);
+      $this->target = entity_load($this->getTargetDefinition()->getEntityTypeId(), $this->id);
     }
     return $this->target;
   }
@@ -99,7 +98,7 @@ public function setValue($value, $notify = TRUE) {
     if (!isset($value) || $value instanceof EntityInterface) {
       $this->target = $value;
     }
-    elseif (!is_scalar($value) || (($constraints = $this->definition->getConstraints()) && empty($constraints['EntityType']))) {
+    elseif (!is_scalar($value) || $this->getTargetDefinition()->getEntityTypeId() === NULL) {
       throw new \InvalidArgumentException('Value is not a valid entity.');
     }
     else {
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index add9c73..3139bdc 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -144,7 +144,7 @@ public function addField($field, $type, $langcode) {
             $entity = $entity_manager
               ->getStorageController($entity_type_id)
               ->create($values);
-            $propertyDefinitions = $entity->$field_name->getPropertyDefinitions();
+            $propertyDefinitions = $entity->$field_name->getFieldDefinition()->getPropertyDefinitions();
 
             // If the column is not yet known, ie. the
             // $node->field_image->entity case then use first property as
@@ -198,14 +198,14 @@ public function addField($field, $type, $langcode) {
           $entity = $entity_manager
             ->getStorageController($entity_type_id)
             ->create($values);
-          $propertyDefinitions = $entity->$specifier->getPropertyDefinitions();
+          $propertyDefinitions = $entity->$specifier->getFieldDefinition()->getPropertyDefinitions();
           $relationship_specifier = $specifiers[$key + 1];
           $next_index_prefix = $relationship_specifier;
         }
         // Check for a valid relationship.
         if (isset($propertyDefinitions[$relationship_specifier]) && $entity->get($specifier)->first()->get('entity') instanceof EntityReference) {
           // If it is, use the entity type.
-          $entity_type_id = $propertyDefinitions[$relationship_specifier]->getConstraint('EntityType');
+          $entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityType();
           $entity_type = $entity_manager->getDefinition($entity_type_id);
           // Add the new entity base table using the table and sql column.
           $join_condition= '%alias.' . $entity_type->getKey('id') . " = $table.$sql_column";
diff --git a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php
new file mode 100644
index 0000000..e5210c6
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\TypedData\EntityDataDefinition.
+ */
+
+namespace Drupal\Core\Entity\TypedData;
+
+use Drupal\Core\TypedData\ComplexDataDefinitionBase;
+
+/**
+ * A typed data definition class for describing entities.
+ */
+class EntityDataDefinition extends ComplexDataDefinitionBase implements EntityDataDefinitionInterface {
+
+  /**
+   * Creates a new entity definition.
+   *
+   * @param string $entity_type_id
+   *   (optional) The ID of the entity type, or NULL if the entity type is
+   *   unknown. Defaults to NULL.
+   *
+   * @return static
+   *   A new EntityDefinition object.
+   */
+  public static function create($entity_type_id = NULL) {
+    $definition = new static(array());
+    // Set the passed entity type.
+    if (isset($entity_type_id)) {
+      $definition->setEntityTypeId($entity_type_id);
+    }
+    return $definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset($this->propertyDefinitions)) {
+      if ($entity_type_id = $this->getEntityTypeId()) {
+        // @todo: Add support for handling multiple bundles.
+        // See https://drupal.org/node/2169813.
+        $bundles = $this->getBundles();
+        $bundle = is_array($bundles) && count($bundles) == 1 ? reset($bundles) : NULL;
+        $this->propertyDefinitions = \Drupal::entityManager()->getFieldDefinitions($entity_type_id, $bundle);
+      }
+      else {
+        // No entity type given.
+        $this->propertyDefinitions = array();
+      }
+    }
+    return $this->propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataType() {
+    $type = 'entity';
+    if ($entity_type = $this->getEntityTypeId()) {
+      $type .= ':' . $entity_type;
+      // Append the bundle only if we know it for sure.
+      if (($bundles = $this->getBundles()) && count($bundles) == 1) {
+        $type .= ':' . reset($bundles);
+      }
+    }
+    return $type;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEntityTypeId() {
+    return $this->getConstraint('EntityType');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntityTypeId($entity_type_id) {
+    return $this->addConstraint('EntityType', $entity_type_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundles() {
+    $bundle = $this->getConstraint('Bundle');
+    return is_string($bundle) ? array($bundle) : $bundle;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setBundles(array $bundles) {
+    return $this->addConstraint('Bundle', $bundles);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinitionInterface.php b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinitionInterface.php
new file mode 100644
index 0000000..9b57384
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinitionInterface.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface.
+ */
+
+namespace Drupal\Core\Entity\TypedData;
+
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+
+/**
+ * Interface for typed data entity definitions.
+ */
+interface EntityDataDefinitionInterface extends ComplexDataDefinitionInterface {
+
+  /**
+   * Returns the entity type ID.
+   *
+   * @return string|null
+   *   The entity type ID, or NULL if the entity type is unknown.
+   */
+  public function getEntityTypeId();
+
+  /**
+   * Sets the entity type ID.
+   *
+   * @param string $entity_type_id
+   *   The entity type to set.
+   *
+   * @return static
+   *   The object itself for chaining.
+   */
+  public function setEntityTypeId($entity_type_id);
+
+  /**
+   * Returns the array of possible entity bundles.
+   *
+   * @return array|null
+   *   The array of possible bundles, or NULL if the bundle is unknown or the
+   *   entity type does not have bundles.
+   */
+  public function getBundles();
+
+  /**
+   * Sets the array of possible entity bundles.
+   *
+   * @param array $bundles
+   *   The bundles to set.
+   *
+   * @return static
+   *   The object itself for chaining.
+   */
+  public function setBundles(array $bundles);
+}
diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php
index e1f698a..630b148 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinition.php
@@ -8,14 +8,30 @@
 namespace Drupal\Core\Field;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\ListDefinition;
+use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
+use Drupal\Core\TypedData\ListDataDefinition;
 use Drupal\field\FieldException;
 
 /**
  * A class for defining entity fields.
  */
-class FieldDefinition extends ListDefinition implements FieldDefinitionInterface {
+class FieldDefinition extends ListDataDefinition implements FieldDefinitionInterface {
+
+  /**
+   * The field type.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * An array of field property definitions.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *
+   * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
+   */
+  protected $propertyDefinitions;
 
   /**
    * The field schema.
@@ -39,15 +55,16 @@ class FieldDefinition extends ListDefinition implements FieldDefinitionInterface
    *   A new field definition object.
    */
   public static function create($type) {
+    $field_definition = new static(array());
+    $field_definition->type = $type;
+    $field_definition->itemDefinition = FieldItemDataDefinition::create($field_definition);
     // Create a definition for the items, and initialize it with the default
     // settings for the field type.
     // @todo Cleanup in https://drupal.org/node/2116341.
-    $item_definition = DataDefinition::create('field_item:' . $type);
     $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
     $default_settings = $field_type_manager->getDefaultSettings($type) + $field_type_manager->getDefaultInstanceSettings($type);
-    $item_definition->setSettings($default_settings);
-
-    return new static(array(), $item_definition);
+    $field_definition->itemDefinition->setSettings($default_settings);
+    return $field_definition;
   }
 
   /**
@@ -75,10 +92,7 @@ public function setName($name) {
    * {@inheritdoc}
    */
   public function getType() {
-    $data_type = $this->getItemDefinition()->getDataType();
-    // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
-    $parts = explode(':', $data_type);
-    return $parts[1];
+    return $this->type;
   }
 
   /**
@@ -128,13 +142,6 @@ public function setSetting($setting_name, $value) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyNames() {
-    return array_keys(\Drupal::typedDataManager()->create($this->getItemDefinition())->getPropertyDefinitions());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function isTranslatable() {
     return !empty($this->definition['translatable']);
   }
@@ -288,6 +295,71 @@ public function getDefaultValue(EntityInterface $entity) {
   /**
    * {@inheritdoc}
    */
+  public function getPropertyDefinition($name) {
+    if (!isset($this->propertyDefinitions)) {
+      $this->getPropertyDefinitions();
+    }
+    if (isset($this->propertyDefinitions[$name])) {
+      return $this->propertyDefinitions[$name];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset($this->propertyDefinitions)) {
+      $class = $this->getFieldItemClass();
+      $this->propertyDefinitions = $class::propertyDefinitions($this);
+    }
+    return $this->propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyNames() {
+    return array_keys($this->getPropertyDefinitions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    $class = $this->getFieldItemClass();
+    return $class::getMainPropertyName();
+  }
+
+  /**
+   * Helper to retrieve the field item class.
+   *
+   * @todo: Remove once getClass() adds in defaults. See
+   * https://drupal.org/node/2116341.
+   */
+  protected function getFieldItemClass() {
+    if ($class = $this->itemDefinition->getClass()) {
+      return $class;
+    }
+    else {
+      $type_definition = \Drupal::typedDataManager()
+        ->getDefinition($this->itemDefinition->getDataType());
+      return $type_definition['class'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __sleep() {
+    // Do not serialize the statically cached property definitions.
+    $vars = get_object_vars($this);
+    unset($vars['propertyDefinitions']);
+    return array_keys($vars);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getTargetEntityTypeId() {
     return isset($this->definition['entity_type']) ? $this->definition['entity_type'] : NULL;
   }
diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
index aba828f..39fdc83 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\Field;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\TypedData\ListDefinitionInterface;
+use Drupal\Core\TypedData\ListDataDefinitionInterface;
 
 /**
  * Defines an interface for entity field definitions.
@@ -51,7 +51,7 @@
  * based on that abstract definition, even though that abstract definition can
  * differ from the concrete definition of any particular node's body field.
  */
-interface FieldDefinitionInterface extends ListDefinitionInterface {
+interface FieldDefinitionInterface extends ListDataDefinitionInterface {
 
   /**
    * Value indicating a field accepts an unlimited number of values.
@@ -256,6 +256,38 @@ public function isMultiple();
   public function getDefaultValue(EntityInterface $entity);
 
   /**
+   * Gets the definition of a contained property.
+   *
+   * @param string $name
+   *   The name of property.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
+   *   The definition of the property or NULL if the property does not exist.
+   */
+  public function getPropertyDefinition($name);
+
+  /**
+   * Gets an array of property definitions of contained properties.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *   An array of property definitions of contained properties, keyed by
+   *   property name.
+   */
+  public function getPropertyDefinitions();
+
+  /**
+   * Returns the name of the main property, if any.
+   *
+   * Some field items consist mainly of one main property, e.g. the value of a
+   * text field or the @code target_id @endcode of an entity reference. If the
+   * field item has no main property, the method returns NULL.
+   *
+   * @return string|null
+   *   The name of the value property, or NULL if there is none.
+   */
+  public function getMainPropertyName();
+
+  /**
    * Returns the ID of the type of the entity this field is attached to.
    *
    * This method should not be confused with EntityInterface::entityType()
diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php
index 125b964..89a15d7 100644
--- a/core/lib/Drupal/Core/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Field/FieldItemBase.php
@@ -17,7 +17,7 @@
  * An entity field item.
  *
  * Entity field items making use of this base class have to implement
- * ComplexDataInterface::getPropertyDefinitions().
+ * the static method propertyDefinitions().
  *
  * @see \Drupal\Core\Field\FieldItemInterface
  */
@@ -26,6 +26,23 @@
   /**
    * {@inheritdoc}
    */
+  public static function createDataDefinition($data_type) {
+    // The data type of a field item is in the form of "field_item:$field_type".
+    $parts = explode(':', $data_type, 2);
+    $field_definition = FieldDefinition::create($parts[1]);
+    return $field_definition->getItemDefinition();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getMainPropertyName() {
+    return 'value';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
     // Initialize computed properties by default, such that they get cloned
@@ -211,11 +228,4 @@ public function delete() { }
    */
   public function deleteRevision() { }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getMainPropertyName() {
-    return 'value';
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Field/FieldItemInterface.php
index bb1c5e5..b6a9653 100644
--- a/core/lib/Drupal/Core/Field/FieldItemInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemInterface.php
@@ -24,6 +24,31 @@
 interface FieldItemInterface extends ComplexDataInterface {
 
   /**
+   * Defines field item properties.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *   An array of property definitions of contained properties, keyed by
+   *   property name.
+   *
+   * @see \Drupal\Core\Field\FieldDefinition
+   */
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition);
+
+  /**
+   * Returns the name of the main property, if any.
+   *
+   * Some field items consist mainly of one main property, e.g. the value of a
+   * text field or the @code target_id @endcode of an entity reference. If the
+   * field item has no main property, the method returns NULL.
+   *
+   * @return string|null
+   *   The name of the value property, or NULL if there is none.
+   *
+   * @see \Drupal\Core\Field\FieldDefinition
+   */
+  public static function getMainPropertyName();
+
+  /**
    * Returns the schema for the field.
    *
    * This method is static because the field schema information is needed on
@@ -40,7 +65,7 @@
    *   following key/value pairs:
    *   - columns: An array of Schema API column specifications, keyed by column
    *     name. The columns need to be a subset of the properties defined in
-   *     getPropertyDefinitions(). It is recommended to avoid having the column
+   *     propertyDefinitions(). It is recommended to avoid having the column
    *     definitions depend on field settings when possible. No assumptions
    *     should be made on how storage engines internally use the original
    *     column name to structure their storage.
@@ -171,20 +196,4 @@ public function delete();
    */
   public function deleteRevision();
 
-  /**
-   * Returns the name of the main property, if any.
-   *
-   * Some field items consist mainly of one main property, e.g. the value of a
-   * text field or the @code target_id @endcode of an entity reference. If the
-   * field item has no main property, the method returns NULL.
-   *
-   * @return string|null
-   *   The name of the value property, or NULL if there is none.
-   *
-   * @todo: Move this to ComplexDataInterface once we improved Typed data to do
-   *   not enforce having all methods on the data objects.
-   *   https://drupal.org/node/2002134
-   */
-  public function getMainPropertyName();
-
 }
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 8afca40..cff6233 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -41,6 +41,15 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
   /**
    * {@inheritdoc}
    */
+  public static function createDataDefinition($data_type) {
+    // The data type of a field item is in the form of "field_item:$field_type".
+    $parts = explode(':', $data_type, 2);
+    return FieldDefinition::create($parts[1]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
     // Always initialize one empty item as most times a value for at least one
@@ -157,20 +166,6 @@ public function setValue($values, $notify = TRUE) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinition($name) {
-    return $this->first()->getPropertyDefinition($name);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPropertyDefinitions() {
-    return $this->first()->getPropertyDefinitions();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function __get($property_name) {
     return $this->first()->__get($property_name);
   }
diff --git a/core/lib/Drupal/Core/Field/FieldItemListInterface.php b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
index 0af2245..abbf970 100644
--- a/core/lib/Drupal/Core/Field/FieldItemListInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
@@ -124,20 +124,6 @@ public function __isset($property_name);
   public function __unset($property_name);
 
   /**
-   * Gets the definition of a property of the first field item.
-   *
-   * @see \Drupal\Core\Field\FieldItemInterface::getPropertyDefinition()
-   */
-  public function getPropertyDefinition($name);
-
-  /**
-   * Gets an array of property definitions of the first field item.
-   *
-   * @see \Drupal\Core\Field\FieldItemInterface::getPropertyDefinitions()
-   */
-  public function getPropertyDefinitions();
-
-  /**
    * Defines custom presave behavior for field values.
    *
    * This method is called before either insert() or update() methods, and
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
index 1d9884f..334a5a6 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
@@ -24,23 +24,13 @@
 class BooleanItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see BooleanItem::getPropertyDefinitions()
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  static $propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('boolean')
+      ->setLabel(t('Boolean value'));
 
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('boolean')
-        ->setLabel(t('Boolean value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DateItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DateItem.php
index c1276dd..4fe58fe 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DateItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DateItem.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'date' entity field type.
@@ -23,26 +24,13 @@
 class DateItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see DateItem::getPropertyDefinitions()
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  static $propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('date')
+      ->setLabel(t('Date value'));
 
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'date',
-        'label' => t('Date value'),
-      );
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
index 9e68719..93fc622 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
@@ -24,24 +24,13 @@
 class EmailItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see EmailItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('email')
+      ->setLabel(t('E-mail value'));
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('email')
-        ->setLabel(t('E-mail value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 31a7d8e..ce449af 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\DataReferenceDefinition;
 
 /**
  * Defines the 'entity_reference' entity field type.
@@ -31,56 +33,46 @@
 class EntityReferenceItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see EntityReferenceItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $settings = $this->definition->getSettings();
-    $target_type = $settings['target_type'];
-
-    // Definitions vary by entity type and bundle, so key them accordingly.
-    $key = $target_type . ':';
-    $key .= isset($settings['target_bundle']) ? $settings['target_bundle'] : '';
-
-    if (!isset(static::$propertyDefinitions[$key])) {
-      $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-      if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
-        // @todo: Lookup the entity type's ID data type and use it here.
-        // https://drupal.org/node/2107249
-        static::$propertyDefinitions[$key]['target_id'] = DataDefinition::create('integer')
-          ->setLabel(t('Entity ID'))
-          ->setConstraints(array(
-            'Range' => array('min' => 0),
-          ));
-      }
-      else {
-        static::$propertyDefinitions[$key]['target_id'] = DataDefinition::create('string')
-          ->setLabel(t('Entity ID'));
-      }
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $settings = $field_definition->getSettings();
+    $target_type_info = \Drupal::entityManager()->getDefinition($settings['target_type']);
 
-      static::$propertyDefinitions[$key]['entity'] = DataDefinition::create('entity_reference')
-        ->setLabel(t('Entity'))
-        ->setDescription(t('The referenced entity'))
-        // The entity object is computed out of the entity ID.
-        ->setComputed(TRUE)
-        ->setReadOnly(FALSE)
+    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
+      // @todo: Lookup the entity type's ID data type and use it here.
+      // https://drupal.org/node/2107249
+      $target_id_definition = DataDefinition::create('integer')
+        ->setLabel(t('Entity ID'))
         ->setConstraints(array(
-          'EntityType' => $settings['target_type'],
+          'Range' => array('min' => 0),
         ));
-
-      if (isset($settings['target_bundle'])) {
-        static::$propertyDefinitions[$key]['entity']->addConstraint('Bundle', $settings['target_bundle']);
-      }
     }
-    return static::$propertyDefinitions[$key];
+    else {
+      $target_id_definition = DataDefinition::create('string')
+        ->setLabel(t('Entity ID'));
+    }
+    $properties['target_id'] = $target_id_definition;
+    $properties['entity'] = DataReferenceDefinition::create('entity')
+      ->setLabel(t('Entity'))
+      ->setDescription(t('The referenced entity'))
+      // The entity object is computed out of the entity ID.
+      ->setComputed(TRUE)
+      ->setReadOnly(FALSE)
+      ->setTargetDefinition(EntityDataDefinition::create($settings['target_type']));
+
+    if (isset($settings['target_bundle'])) {
+      $properties['entity']->getTargetDefinition()->addConstraint('Bundle', $settings['target_bundle']);
+    }
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getMainPropertyName() {
+    return 'target_id';
   }
 
   /**
@@ -193,14 +185,7 @@ public function onChange($property_name) {
     }
     parent::onChange($property_name);
   }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getMainPropertyName() {
-    return 'target_id';
-  }
-
+  
   /**
    * {@inheritdoc}
    */
@@ -240,5 +225,4 @@ public function preSave() {
   public function hasUnsavedEntity() {
     return $this->target_id === NULL && ($entity = $this->entity) && $entity->isNew();
   }
-
 }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
index 045af1c..c53f639 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
@@ -24,24 +24,13 @@
 class FloatItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see IntegerItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('float')
+      ->setLabel(t('Float value'));
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('float')
-        ->setLabel(t('Float value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index ec49cdb..0a7a8cf 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -24,24 +24,13 @@
 class IntegerItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see IntegerItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('integer')
+      ->setLabel(t('Integer value'));
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
-        ->setLabel(t('Integer value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
index 3bae65b..f764710 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Language\Language;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\DataReferenceDefinition;
 
 /**
  * Defines the 'language' entity field item.
@@ -30,30 +31,20 @@
 class LanguageItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see LanguageItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Language code'));
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Language code'));
 
-      static::$propertyDefinitions['language'] = DataDefinition::create('language_reference')
-        ->setLabel(t('Language object'))
-        ->setDescription(t('The referenced language'))
+    $properties['language'] = DataReferenceDefinition::create('language')
+      ->setLabel(t('Language object'))
+      ->setDescription(t('The referenced language'))
       // The language object is retrieved via the language code.
-        ->setComputed(TRUE)
-        ->setReadOnly(FALSE);
-    }
-    return static::$propertyDefinitions;
+      ->setComputed(TRUE)
+      ->setReadOnly(FALSE);
+
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
index 698e5b8..50f38ed 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'map' entity field type.
@@ -16,7 +17,7 @@
  * @FieldType(
  *   id = "map",
  *   label = @Translation("Map"),
- *   description = @Translation("An entity field containing a map value."),
+ *   description = @Translation("An entity field for storing a serialized array of values."),
  *   configurable = FALSE
  * )
  */
@@ -25,6 +26,16 @@ class MapItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Serialized values'));
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public static function schema(FieldDefinitionInterface $field_definition) {
     return array(
       'columns' => array(
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index c5f6c77..5a32aa6 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -27,24 +27,13 @@
 class StringItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see StringItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Text value'));
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Text value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index d8c0b32..0fa6820 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -30,23 +30,13 @@
 class UriItem extends StringItem {
 
   /**
-   * Field definitions of the contained properties.
-   *
-   * @see self::getPropertyDefinitions()
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  static $propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('uri')
+      ->setLabel(t('URI value'));
 
-  /**
-   * Implements ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    if (!isset(self::$propertyDefinitions)) {
-      self::$propertyDefinitions['value'] = DataDefinition::create('uri')
-        ->setLabel(t('URI value'));
-    }
-    return self::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php
new file mode 100644
index 0000000..461856f
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\TypedData\FieldItemDataDefinition.
+ */
+
+namespace Drupal\Core\Field\TypedData;
+
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+
+/**
+ * A typed data definition class for defining field items.
+ *
+ * This class is just a small wrapper around field definitions to expose
+ * metadata about field item's via the Typed Data API. As the work is done
+ * by the field definitions, this class does not benefit and thus does not
+ * extend from MapDefinition or ComplexDataDefinitionBase.
+ */
+class FieldItemDataDefinition extends DataDefinition implements ComplexDataDefinitionInterface {
+
+  /**
+   * The field definition the item definition belongs to.
+   *
+   * @var \Drupal\Core\Field\FieldDefinitionInterface
+   */
+  protected $fieldDefinition;
+
+  /**
+   * Creates a new field item definition.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition the item definition belongs to.
+   *
+   * @return static
+   *   A new FieldItemDefinition object.
+   */
+  public static function create($field_definition) {
+    $definition['type'] = 'field_item:' . $field_definition->getType();
+    $item_definition = new static($definition);
+    $item_definition->fieldDefinition = $field_definition;
+    return $item_definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinition($name) {
+    return $this->fieldDefinition->getPropertyDefinition($name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    return $this->fieldDefinition->getPropertyDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return $this->fieldDefinition->getMainPropertyName();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionBase.php b/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionBase.php
new file mode 100644
index 0000000..ac70ef5
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionBase.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\ComplexDataDefinitionBase.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Base class for complex data definitions.
+ */
+abstract class ComplexDataDefinitionBase extends DataDefinition implements ComplexDataDefinitionInterface {
+
+  /**
+   * An array of data definitions.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *
+   * @see ComplexDataDefinitionBase::getPropertyDefinitions()
+   */
+  protected $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinition($name) {
+    if (!isset($this->propertyDefinitions)) {
+      $this->getPropertyDefinitions();
+    }
+    if (isset($this->propertyDefinitions[$name])) {
+      return $this->propertyDefinitions[$name];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __sleep() {
+    // Do not serialize the cached property definitions.
+    $vars = get_object_vars($this);
+    unset($vars['propertyDefinitions']);
+    return array_keys($vars);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function getPropertyDefinitions();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionInterface.php
new file mode 100644
index 0000000..efa5fdf
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataDefinitionInterface.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\ComplexDataDefinitionInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for complex data definitions.
+ *
+ * @see \Drupal\Core\TypedData\ComplexDataInterface
+ */
+interface ComplexDataDefinitionInterface extends DataDefinitionInterface  {
+
+  /**
+   * Gets the definition of a contained property.
+   *
+   * @param string $name
+   *   The name of property.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
+   *   The definition of the property or NULL if the property does not exist.
+   */
+  public function getPropertyDefinition($name);
+
+  /**
+   * Gets an array of property definitions of contained properties.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *   An array of property definitions of contained properties, keyed by
+   *   property name.
+   */
+  public function getPropertyDefinitions();
+
+  /**
+   * Returns the name of the main property, if any.
+   *
+   * Some field items consist mainly of one main property, e.g. the value of a
+   * text field or the @code target_id @endcode of an entity reference. If the
+   * field item has no main property, the method returns NULL.
+   *
+   * @return string|null
+   *   The name of the value property, or NULL if there is none.
+   */
+  public function getMainPropertyName();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 438ea6e..452b602 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -18,6 +18,8 @@
  *
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
+ *
+ * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface
  */
 interface ComplexDataInterface extends \Traversable, TypedDataInterface  {
 
@@ -92,26 +94,6 @@ public function getPropertyValues();
   public function setPropertyValues($values);
 
   /**
-   * Gets the definition of a contained property.
-   *
-   * @param string $name
-   *   The name of property.
-   *
-   * @return array|FALSE
-   *   The definition of the property or FALSE if the property does not exist.
-   */
-  public function getPropertyDefinition($name);
-
-  /**
-   * Gets an array of property definitions of contained properties.
-   *
-   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
-   *   An array of property definitions of contained properties, keyed by
-   *   property name.
-   */
-  public function getPropertyDefinitions();
-
-  /**
    * Determines whether the data structure is empty.
    *
    * @return boolean
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php
index 89d9f4e..e7bf25e 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\TypedData;
 
 /**
- * A class for defining data based on defined data types.
+ * A typed data definition class for defining data based on defined data types.
  */
 class DataDefinition implements DataDefinitionInterface, \ArrayAccess {
 
@@ -110,7 +110,7 @@ public function setDescription($description) {
    * {@inheritdoc}
    */
   public function isList() {
-    return ($this instanceof ListDefinitionInterface);
+    return ($this instanceof ListDataDefinitionInterface);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
index e026fe8..e8079b4 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
@@ -14,7 +14,15 @@
  * For example, a plugin could describe its parameters using data definitions
  * in order to specify what kind of data is required for it.
  *
+ * Definitions that describe lists or complex data have to implement the
+ * respective interfaces, such that the metadata about contained list items or
+ * properties can be retrieved from the definition.
+ *
  * @see \Drupal\Core\TypedData\DataDefinition
+ * @see \Drupal\Core\TypedData\ListDefinitionInterface
+ * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface
+ * @see \Drupal\Core\TypedData\DataReferenceDefinitionInterface
+ * @see \Drupal\Core\TypedData\TypedDataInterface
  */
 interface DataDefinitionInterface {
 
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceBase.php b/core/lib/Drupal/Core/TypedData/DataReferenceBase.php
index 5a2d597..9ab5aa5 100644
--- a/core/lib/Drupal/Core/TypedData/DataReferenceBase.php
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceBase.php
@@ -10,9 +10,14 @@
 /**
  * Base class for typed data references.
  *
- * Implementing classes have to implement at least
- * \Drupal\Core\TypedData\DataReferenceInterface::getTargetDefinition() and
+ * Data types based on this base class need to be named
+ * "{TARGET_TYPE}_reference", whereas {TARGET_TYPE} is the referenced data type.
+ * For example, an entity reference data type would have to be named
+ * "entity_reference".
+ * Beside that, implementing classes have to implement at least
  * \Drupal\Core\TypedData\DataReferenceInterface::getTargetIdentifier().
+ *
+ * @see \Drupal\Core\TypedData\DataReferenceDefinition
  */
 abstract class DataReferenceBase extends TypedData implements DataReferenceInterface  {
 
@@ -26,6 +31,17 @@
   /**
    * {@inheritdoc}
    */
+  public static function createDataDefinition($data_type) {
+    if (substr($data_type, -strlen('_reference')) != '_reference') {
+      throw new \InvalidArgumentException('Data type must be of the form "{TARGET_TYPE}_reference"');
+    }
+    // Cut of the _reference suffix.
+    return DataReferenceDefinition::create(substr($data_type, 0, strlen($data_type) - strlen('_reference')));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getTarget() {
     return $this->target;
   }
@@ -43,7 +59,7 @@ public function getValue() {
    * {@inheritdoc}
    */
   public function setValue($value, $notify = TRUE) {
-    $this->target = \Drupal::typedDataManager()->create($this->getTargetDefinition(), $value);
+    $this->target = \Drupal::typedDataManager()->create($this->definition->getTargetDefinition(), $value);
     // Notify the parent of any changes.
     if ($notify && isset($this->parent)) {
       $this->parent->onChange($this->name);
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php b/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php
new file mode 100644
index 0000000..489b56f
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceDefinition.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataReferenceDefinition.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * A typed data definition class for defining references.
+ *
+ * Note that this definition class assumes that the data type for referencing
+ * a certain target type is named "{TARGET_TYPE}_reference".
+ *
+ * @see \Drupal\Core\TypedData\DataReferenceBase
+ */
+class DataReferenceDefinition extends DataDefinition implements DataReferenceDefinitionInterface {
+
+  /**
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface
+   */
+  protected $targetDefinition;
+
+  /**
+   * Creates a new data reference definition.
+   *
+   * @param string $target_data_type
+   *   The data type of the referenced data.
+   *
+   * @return static
+   *   A new DataReferenceDefinition object.
+   */
+  public static function create($type) {
+    // This assumes implementations use a "TYPE_reference" naming pattern.
+    $definition = parent::create($type . '_reference');
+    return $definition->setTargetDefinition(\Drupal::typedDataManager()->createDataDefinition($type));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetDefinition() {
+    return $this->targetDefinition;
+  }
+
+  /**
+   * Sets the definition of the referenced data.
+   *
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
+   *   The target definition to set.
+   *
+   * @return static
+   *   The object itself for chaining.
+   */
+  public function setTargetDefinition(DataDefinitionInterface $definition) {
+    $this->targetDefinition = $definition;
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataReferenceDefinitionInterface.php
new file mode 100644
index 0000000..334cc9b
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceDefinitionInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataReferenceDefinitionInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for typed data references.
+ *
+ * @see \Drupal\Core\TypedData\DataReferenceDefinition
+ * @see \Drupal\Core\TypedData\DataReferenceInterface
+ */
+interface DataReferenceDefinitionInterface extends DataDefinitionInterface  {
+
+  /**
+   * Gets the data definition of the referenced data.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   *   The data definition of the referenced data.
+   */
+  public function getTargetDefinition();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php
index 501f9d2..8fe88fc 100644
--- a/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php
@@ -9,18 +9,12 @@
 
 /**
  * Interface for typed data references.
+ *
+ * @see \Drupal\Core\TypedData\DataReferenceDefinitionInterface
  */
 interface DataReferenceInterface  {
 
   /**
-   * Gets the data definition of the referenced data.
-   *
-   * @return array
-   *   The data definition of the referenced data.
-   */
-  public function getTargetDefinition();
-
-  /**
    * Gets the referenced data.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface|null
@@ -35,4 +29,5 @@ public function getTarget();
    *   The identifier of the referenced data, or NULL if the reference is unset.
    */
   public function getTargetIdentifier();
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/ListDefinition.php b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php
similarity index 82%
rename from core/lib/Drupal/Core/TypedData/ListDefinition.php
rename to core/lib/Drupal/Core/TypedData/ListDataDefinition.php
index 4963107..4d06044 100644
--- a/core/lib/Drupal/Core/TypedData/ListDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php
@@ -2,15 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\Core\TypedData\ListDefinition.
+ * Contains \Drupal\Core\TypedData\ListDataDefinition.
  */
 
 namespace Drupal\Core\TypedData;
 
 /**
- * A class for defining data based on defined data types.
+ * A typed data definition class for defining lists.
  */
-class ListDefinition extends DataDefinition implements ListDefinitionInterface {
+class ListDataDefinition extends DataDefinition implements ListDataDefinitionInterface {
 
   /**
    * The data definition of a list item.
@@ -25,7 +25,7 @@ class ListDefinition extends DataDefinition implements ListDefinitionInterface {
    * @param string $item_type
    *   The data type of the list items; e.g., 'string', 'integer' or 'any'.
    *
-   * @return \Drupal\Core\TypedData\ListDefinition
+   * @return \Drupal\Core\TypedData\ListDataDefinition
    *   A new List Data Definition object.
    */
   public static function create($item_type) {
@@ -34,12 +34,10 @@ public static function create($item_type) {
 
   /**
    * {@inheritdoc}
-   *
-   * @param
    */
   public function __construct(array $definition = array(), DataDefinitionInterface $item_definition = NULL) {
-    parent::__construct($definition);
-    $this->itemDefinition = isset($item_definition) ? $item_definition : DataDefinition::create('any');
+    $this->definition = $definition;
+    $this->itemDefinition = $item_definition;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/ListDataDefinitionInterface.php
similarity index 67%
rename from core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php
rename to core/lib/Drupal/Core/TypedData/ListDataDefinitionInterface.php
index c07d60e..7c7b269 100644
--- a/core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListDataDefinitionInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\Core\TypedData\ListDefinitionInterface.
+ * Contains \Drupal\Core\TypedData\ListDataDefinitionInterface.
  */
 
 namespace Drupal\Core\TypedData;
@@ -12,8 +12,11 @@
  *
  * This interface is present on a data definition if it describes a list. The
  * actual lists implement the \Drupal\Core\TypedData\ListInterface.
+ *
+ * @see \Drupal\Core\TypedData\ListDefinition
+ * @see \Drupal\Core\TypedData\ListInterface
  */
-interface ListDefinitionInterface extends DataDefinitionInterface {
+interface ListDataDefinitionInterface extends DataDefinitionInterface {
 
   /**
    * Gets the data definition of an item of the list.
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 3f8972b..65af58e 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -15,10 +15,23 @@
  *
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
+ *
+ * @see \Drupal\Core\TypedData\ListDefinitionInterface
  */
 interface ListInterface extends TypedDataInterface, \ArrayAccess, \Countable, \Traversable {
 
   /**
+   * Creates a new list definition.
+   *
+   * @param string $data_type
+   *   The data type of the list items.
+   *
+   * @return \Drupal\Core\TypedData\ListDataDefinitionInterface
+   *   A list definition for the given data type.
+   */
+  public static function createDataDefinition($data_type);
+
+  /**
    * Determines whether the list contains any non-empty items.
    *
    * @return boolean
diff --git a/core/lib/Drupal/Core/TypedData/MapDataDefinition.php b/core/lib/Drupal/Core/TypedData/MapDataDefinition.php
new file mode 100644
index 0000000..6a9b3ae
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/MapDataDefinition.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\MapDataDefinition.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * A typed data definition class for defining maps.
+ */
+class MapDataDefinition extends ComplexDataDefinitionBase {
+
+  /**
+   * The name of the main property, or NULL if there is none.
+   *
+   * @var string
+   */
+  protected $mainPropertyName = NULL;
+
+  /**
+   * Creates a new map definition.
+   *
+   * @param string $type
+   *   (optional) The data type of the map. Defaults to 'map'.
+   *
+   * @return static
+   *   A new MapDefinition object.
+   */
+  public static function create($type = 'map') {
+    $definition['type'] = $type;
+    return new static($definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset($this->propertyDefinitions)) {
+      $this->propertyDefinitions = array();
+    }
+    return $this->propertyDefinitions;
+  }
+
+  /**
+   * Sets the definition of a map property.
+   *
+   * @param string $name
+   *   The name of the property to define.
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface|null $definition
+   *   (optional) The property definition to set, or NULL to unset it.
+   *
+   * @return static
+   *   The object itself for chaining.
+   */
+  public function setPropertyDefinition($name, DataDefinitionInterface $definition = NULL) {
+    if (isset($definition)) {
+      $this->propertyDefinitions[$name] = $definition;
+    }
+    else {
+      unset($this->propertyDefinitions[$name]);
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return $this->mainPropertyName;
+  }
+
+  /**
+   * Sets the main property name.
+   *
+   * @param string|null $name
+   *   The name of the main property, or NULL if there is none.
+   *
+   * @return static
+   *   The object itself for chaining.
+   */
+  public function setMainPropertyName($name) {
+    $this->mainPropertyName = $name;
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
index d2b1a06..dbcdb2b 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\TypedData\Plugin\DataType;
 
 use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListDataDefinition;
 use Drupal\Core\TypedData\ListInterface;
 use Drupal\Core\TypedData\TypedData;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -35,6 +36,13 @@ class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
   protected $list = array();
 
   /**
+   * {@inheritdoc}
+   */
+  public static function createDataDefinition($data_type) {
+    return ListDataDefinition::create($data_type);
+  }
+
+  /**
    * Overrides \Drupal\Core\TypedData\TypedData::getValue().
    */
   public function getValue() {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/LanguageReference.php
similarity index 66%
rename from core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php
rename to core/lib/Drupal/Core/TypedData/Plugin/DataType/LanguageReference.php
index f0c352c..d0cfae5 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/LanguageReference.php
@@ -2,18 +2,17 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Plugin\DataType\LanguageReference.
+ * Contains \Drupal\Core\TypedData\Plugin\DataType\LanguageReference.
  */
 
-namespace Drupal\Core\Entity\Plugin\DataType;
+namespace Drupal\Core\TypedData\Plugin\DataType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\DataReferenceBase;
 
 /**
  * Defines the 'language_reference' data type.
  *
- * This serves as 'language' property of entity reference field items and gets
+ * This serves as 'language' property of language field items and gets
  * its value set from the parent, i.e. LanguageItem.
  *
  * The plain value is the language object, i.e. an instance of
@@ -30,15 +29,9 @@ class LanguageReference extends DataReferenceBase {
   /**
    * {@inheritdoc}
    */
-  public function getTargetDefinition() {
-    return DataDefinition::create('language');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getTargetIdentifier() {
     $language = $this->getTarget();
     return isset($language) ? $language->id() : NULL;
   }
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
index ab66442..6ef655f 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Plugin\DataType;
 
-use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\MapDataDefinition;
 use Drupal\Core\TypedData\TypedData;
 use Drupal\Core\TypedData\ComplexDataInterface;
 
@@ -29,6 +29,13 @@
 class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface {
 
   /**
+   * The data definition.
+   *
+   * @var \Drupal\Core\TypedData\ComplexDataDefinitionInterface
+   */
+  protected $definition;
+
+  /**
    * An array of values for the contained properties.
    *
    * @var array
@@ -36,21 +43,28 @@ class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface
   protected $values = array();
 
   /**
-   * The array of properties, each implementing the TypedDataInterface.
+   * The array of properties.
    *
-   * @var array
+   * @var \Drupal\Core\TypedData\TypedDataInterface[]
    */
   protected $properties = array();
 
   /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $definitions = array();
-    foreach ($this->values as $name => $value) {
-      $definitions[$name] = DataDefinition::create('any');
-    }
-    return $definitions;
+  public static function createDataDefinition($data_type) {
+    return MapDataDefinition::create($data_type);
+  }
+
+  /**
+   * Gets an array of property definitions of contained properties.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *   An array of property definitions of contained properties, keyed by
+   *   property name.
+   */
+  protected function getPropertyDefinitions() {
+    return $this->definition->getPropertyDefinitions();
   }
 
   /**
@@ -128,7 +142,7 @@ public function get($property_name) {
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
    */
   public function set($property_name, $value, $notify = TRUE) {
-    if ($this->getPropertyDefinition($property_name)) {
+    if ($this->definition->getPropertyDefinition($property_name)) {
       $this->get($property_name)->setValue($value, $notify);
     }
     else {
@@ -182,19 +196,6 @@ public function getIterator() {
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
-   */
-  public function getPropertyDefinition($name) {
-    $definitions = $this->getPropertyDefinitions();
-    if (isset($definitions[$name])) {
-      return $definitions[$name];
-    }
-    else {
-      return FALSE;
-    }
-  }
-
-  /**
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
    */
   public function isEmpty() {
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 1597c93..2dda8d2 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -39,6 +39,13 @@
   protected $parent;
 
   /**
+   * {@inheritdoc}
+   */
+  public static function createDataDefinition($data_type) {
+    return DataDefinition::create($data_type);
+  }
+
+  /**
    * Constructs a TypedData object given its definition and context.
    *
    * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
index 4d7b066..1acb970 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
@@ -11,10 +11,28 @@
 
 /**
  * Interface for typed data objects.
+ *
+ * @see \Drupal\Core\TypedData\DataDefinitionInterface
  */
 interface TypedDataInterface {
 
   /**
+   * Creates a new data definition object.
+   *
+   * This method takes care of creating data definition objects based on the
+   * right definition class for its data type and gets usually called by the
+   * manager; i.e.,
+   * \Drupal\Core\TypedData\TypedDataManager::createDataDefinition().
+   *
+   * @param string $data_type
+   *   The data type, for which a data definition should be created.
+   *
+   * @return static
+   *   A data definition for the given data type.
+   */
+  public static function createDataDefinition($data_type);
+
+  /**
    * Gets the data definition.
    *
    * @return \Drupal\Core\TypedData\DataDefinitionInterface
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index e060cd3..b7b7702 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -133,6 +133,43 @@ public function create(DataDefinitionInterface $definition, $value = NULL, $name
   }
 
   /**
+   * Creates a new data definition object.
+   *
+   * While data definitions objects may be created directly if the definition
+   * class used by a data type is known, this method allows the creation of data
+   * definitions for any given data type.
+   *
+   * E.g., if a definition for a map is to be created, the following code
+   * could be used instead of calling this method with the argument 'map':
+   * @code
+   *   $map_definition = \Drupal\Core\TypedData\MapDefinition::create();
+   * @endcode
+   *
+   * @param string $data_type
+   *   The data type, for which a data definition should be created.
+   * @param bool $list
+   *   (optional) Whether to define a single item of the given type (FALSE), or
+   *   a list of items (TRUE). Defaults to FALSE.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|\Drupal\Core\TypedData\ListDataDefinitionInterface
+   *   A data or list definition for the given data type.
+   */
+  public function createDataDefinition($data_type, $list = FALSE) {
+    $type_definition = $this->getDefinition($data_type);
+    if (!isset($type_definition)) {
+      throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $data_type)));
+    }
+    // A single 'list' item is the same as a list of 'any' items. Always handle
+    // it as the latter, such that the "list" class can always act as a list.
+    if ($data_type == 'list' && !$list) {
+      $data_type = 'any';
+      $list = TRUE;
+    }
+    $class = $list ? $type_definition['list_class'] : $type_definition['class'];
+    return $class::createDataDefinition($data_type);
+  }
+
+  /**
    * Implements \Drupal\Component\Plugin\PluginManagerInterface::getInstance().
    *
    * @param array $options
@@ -191,7 +228,7 @@ public function getPropertyInstance(TypedDataInterface $object, $property_name,
     $definition = $object->getRoot()->getDefinition();
     // If the definition is a list, we need to look at the data type and the
     // settings of its item definition.
-    if ($definition instanceof ListDefinition) {
+    if ($definition instanceof ListDataDefinition) {
       $definition = $definition->getItemDefinition();
     }
     $key = $definition->getDataType();
@@ -209,7 +246,7 @@ public function getPropertyInstance(TypedDataInterface $object, $property_name,
       // Create the initial prototype. For that we need to fetch the definition
       // of the to be created property instance from the parent.
       if ($object instanceof ComplexDataInterface) {
-        $definition = $object->getPropertyDefinition($property_name);
+        $definition = $object->getDefinition()->getPropertyDefinition($property_name);
       }
       elseif ($object instanceof ListInterface) {
         $definition = $object->getItemDefinition();
@@ -365,6 +402,12 @@ public function getConstraints(DataDefinitionInterface $definition) {
     if (is_subclass_of($class,'Drupal\Core\TypedData\AllowedValuesInterface')) {
       $constraints[] = $validation_manager->create('AllowedValues', array());
     }
+    // Add any constraints about referenced data.
+    if ($definition instanceof DataReferenceDefinitionInterface) {
+      foreach ($definition->getTargetDefinition()->getConstraints() as $name => $options) {
+        $constraints[] = $validation_manager->create($name, $options);
+      }
+    }
 
     return $constraints;
   }
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index cb98f72..06fc6c3 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -413,7 +413,7 @@ function comment_new_page_count($num_comments, $new_replies, EntityInterface $en
 function comment_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
   // Add the comment page number to the cache key if render caching is enabled.
   if (isset($build['#cache']) && isset($build['#cache']['keys']) && \Drupal::request()->query->has('page')) {
-    foreach ($entity->getPropertyDefinitions() as $field_name => $definition) {
+    foreach ($entity->getFieldDefinitions() as $field_name => $definition) {
       if (isset($build[$field_name]) && $definition->getType() === 'comment') {
         $display_options = $display->getComponent($field_name);
         $pager_id = $display_options['settings']['pager_id'];
@@ -887,7 +887,7 @@ function comment_translation_configuration_element_submit($form, &$form_state) {
 /**
  * Implements hook_entity_load().
  *
- * @see \Drupal\comment\Plugin\Field\FieldType\CommentItem::getPropertyDefinitions()
+ * @see \Drupal\comment\Plugin\Field\FieldType\CommentItem::propertyDefinitions()
  */
 function comment_entity_load($entities, $entity_type) {
   if (!\Drupal::service('comment.manager')->getFields($entity_type)) {
diff --git a/core/modules/comment/comment.tokens.inc b/core/modules/comment/comment.tokens.inc
index ecff337..a4c9af1 100644
--- a/core/modules/comment/comment.tokens.inc
+++ b/core/modules/comment/comment.tokens.inc
@@ -257,7 +257,7 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
         case 'comment-count':
           $count = 0;
           $fields = array_keys(\Drupal::service('comment.manager')->getFields($entity->getEntityTypeId()));
-          $definitions = array_keys($entity->getPropertyDefinitions());
+          $definitions = array_keys($entity->getFieldDefinitions());
           $valid_fields = array_intersect($fields, $definitions);
           foreach ($valid_fields as $field_name) {
             $count += $entity->get($field_name)->comment_count;
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFieldNameItem.php b/core/modules/comment/lib/Drupal/comment/CommentFieldNameItem.php
index a90495d..0f6eae2 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFieldNameItem.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFieldNameItem.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\comment;
 
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
 use Drupal\Core\TypedData\DataDefinition;
 
@@ -16,26 +17,15 @@
 class CommentFieldNameItem extends StringItem {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see self::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('String value'))
+      ->setClass('\Drupal\comment\CommentFieldNameValue')
+      ->setComputed(TRUE);
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('String value'))
-        ->setClass('\Drupal\comment\CommentFieldNameValue')
-        ->setComputed(TRUE);
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index c96fe61..2c8b2c1 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -226,7 +226,7 @@ public function form(array $form, array &$form_state) {
     // Add internal comment properties.
     $original = $comment->getUntranslated();
     foreach (array('cid', 'pid', 'entity_id', 'entity_type', 'field_id', 'uid', 'langcode') as $key) {
-      $key_name = key($comment->$key->first()->getPropertyDefinitions());
+      $key_name = key($comment->$key->first()->getDefinition()->getPropertyDefinitions());
       $form[$key] = array('#type' => 'value', '#value' => $original->$key->{$key_name});
     }
 
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldType/CommentItem.php b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldType/CommentItem.php
index a43e338..ae4f4cb 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldType/CommentItem.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldType/CommentItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\comment\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\ConfigFieldItemBase;
 
 /**
@@ -33,39 +33,31 @@
 class CommentItem extends ConfigFieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  public static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['status'] = DataDefinition::create('integer')
-        ->setLabel(t('Comment status value'));
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['status'] = DataDefinition::create('integer')
+      ->setLabel(t('Comment status value'));
 
-      static::$propertyDefinitions['cid'] = DataDefinition::create('integer')
-        ->setLabel(t('Last comment ID'));
+    $properties['cid'] = DataDefinition::create('integer')
+      ->setLabel(t('Last comment ID'));
 
-      static::$propertyDefinitions['last_comment_timestamp'] = DataDefinition::create('integer')
-        ->setLabel(t('Last comment timestamp'))
-        ->setDescription(t('The time that the last comment was created.'));
+    $properties['last_comment_timestamp'] = DataDefinition::create('integer')
+      ->setLabel(t('Last comment timestamp'))
+      ->setDescription(t('The time that the last comment was created.'));
 
-      static::$propertyDefinitions['last_comment_name'] = DataDefinition::create('string')
-        ->setLabel(t('Last comment name'))
-        ->setDescription(t('The name of the user posting the last comment.'));
+    $properties['last_comment_name'] = DataDefinition::create('string')
+      ->setLabel(t('Last comment name'))
+      ->setDescription(t('The name of the user posting the last comment.'));
 
-      static::$propertyDefinitions['last_comment_uid'] = DataDefinition::create('integer')
-        ->setLabel(t('Last comment user ID'));
+    $properties['last_comment_uid'] = DataDefinition::create('integer')
+      ->setLabel(t('Last comment user ID'));
 
-      static::$propertyDefinitions['comment_count'] = DataDefinition::create('integer')
-        ->setLabel(t('Number of comments'))
-        ->setDescription(t('The number of comments.'));
-    }
-    return static::$propertyDefinitions;
+    $properties['comment_count'] = DataDefinition::create('integer')
+      ->setLabel(t('Number of comments'))
+      ->setDescription(t('The number of comments.'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index aea9349..1889d5b 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -658,7 +658,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
     // Handle fields shared between translations when there is at least one
     // translation available or a new one is being created.
     if (!$entity->isNew() && (!isset($translations[$form_langcode]) || count($translations) > 1)) {
-      foreach ($entity->getPropertyDefinitions() as $property_name => $definition) {
+      foreach ($entity->getFieldDefinitions() as $property_name => $definition) {
         if (isset($form[$property_name])) {
           $form[$property_name]['#multilingual'] = $definition->isTranslatable();
         }
diff --git a/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
index 43aa550..1e15fad 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
@@ -40,29 +40,20 @@ class DateTimeItem extends ConfigFieldItemBase implements PrepareCacheInterface
   const DATETIME_TYPE_DATETIME = 'datetime';
 
   /**
-   * Field definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('datetime_iso8601')
-        ->setLabel(t('Date value'));
-
-      static::$propertyDefinitions['date'] = DataDefinition::create('datetime_computed')
-        ->setLabel(t('Computed date'))
-        ->setDescription(t('The computed DateTime object.'))
-        ->setComputed(TRUE)
-        ->setClass('\Drupal\datetime\DateTimeComputed')
-        ->setSetting('date source', 'value');
-    }
-
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('datetime_iso8601')
+      ->setLabel(t('Date value'));
+
+    $properties['date'] = DataDefinition::create('datetime_computed')
+      ->setLabel(t('Computed date'))
+      ->setDescription(t('The computed DateTime object.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\datetime\DateTimeComputed')
+      ->setSetting('date source', 'value');
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 87732dc..6f258ae 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -173,7 +173,7 @@ function edit_preprocess_field(&$variables) {
 
   // Fields that are not part of the entity (i.e. dynamically injected "pseudo
   // fields") and computed fields are not editable.
-  $definition = $entity->getPropertyDefinition($element['#field_name']);
+  $definition = $entity->getFieldDefinition($element['#field_name']);
   if ($definition && !$definition->isComputed()) {
     $variables['attributes']['data-edit-field-id'] = $entity->getEntityTypeId() . '/' . $entity->id() . '/' . $element['#field_name'] . '/' . $element['#language'] . '/' . $element['#view_mode'];
   }
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 544ff2e..fde8db0 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -628,14 +628,14 @@ function _editor_get_file_uuids_by_field(EntityInterface $entity) {
  *   The names of the fields on this entity that have text processing enabled.
  */
 function _editor_get_processed_text_fields(ContentEntityInterface $entity) {
-  $properties = $entity->getPropertyDefinitions();
-  if (empty($properties)) {
+  $fields = $entity->getFieldDefinitions();
+  if (empty($fields)) {
     return array();
   }
 
   // Find all configurable fields, because only they could have a
   // text_processing setting.
-  $configurable_fields = array_keys(array_filter($properties, function ($definition) {
+  $configurable_fields = array_keys(array_filter($fields, function ($definition) {
     return $definition->isConfigurable();
   }));
   if (empty($configurable_fields)) {
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
index f2eb699..f9643b0 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
@@ -76,40 +76,25 @@ public function getSettableOptions(AccountInterface $account = NULL) {
   }
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see ConfigurableEntityReferenceItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $settings = $this->definition->getSettings();
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $settings = $field_definition->getSettings();
     $target_type = $settings['target_type'];
 
-    // Definitions vary by entity type and bundle, so key them accordingly.
-    $key = $target_type . ':';
-    $key .= isset($settings['target_bundle']) ? $settings['target_bundle'] : '';
-
-    if (!isset(static::$propertyDefinitions[$key])) {
-      // Call the parent to define the target_id and entity properties.
-      parent::getPropertyDefinitions();
-
-      // Only add the revision ID property if the target entity type supports
-      // revisions.
-      $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-      if ($target_type_info->hasKey('revision') && $target_type_info->getRevisionTable()) {
-        static::$propertyDefinitions[$key]['revision_id'] = DataDefinition::create('integer')
-          ->setLabel(t('Revision ID'))
-          ->setConstraints(array('Range' => array('min' => 0)));
-      }
+    // Call the parent to define the target_id and entity properties.
+    $properties = parent::propertyDefinitions($field_definition);
+
+    // Only add the revision ID property if the target entity type supports
+    // revisions.
+    $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
+    if ($target_type_info->hasKey('revision') && $target_type_info->getRevisionTable()) {
+      $properties['revision_id'] = DataDefinition::create('integer')
+        ->setLabel(t('Revision ID'))
+        ->setConstraints(array('Range' => array('min' => 0)));
     }
 
-    return static::$propertyDefinitions[$key];
+    return $properties;
   }
 
   /**
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
index f7b74e4..c3c38cd 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
@@ -102,7 +102,7 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
 
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
     if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
-      // @todo Use Entity::getPropertyDefinitions() when all entity types are
+      // @todo Use Entity::getFieldDefinitions() when all entity types are
       // converted to the new Field API.
       $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_type->getBaseTable()));
       foreach (field_info_instances($target_type) as $bundle_instances) {
diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php
index 69c7551..791fc3b 100644
--- a/core/modules/field/lib/Drupal/field/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -11,7 +11,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
-use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
 use Drupal\field\FieldException;
 use Drupal\field\FieldInterface;
 
@@ -192,6 +192,15 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $original = NULL;
 
   /**
+   * An array of field property definitions.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
+   *
+   * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
+   */
+  protected $propertyDefinitions;
+
+  /**
    * The data definition of a field item.
    *
    * @var \Drupal\Core\TypedData\DataDefinition
@@ -545,14 +554,6 @@ public function getSetting($setting_name) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyNames() {
-    $schema = $this->getSchema();
-    return array_keys($schema['columns']);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function isTranslatable() {
     return $this->translatable;
   }
@@ -777,10 +778,65 @@ public function getConstraint($constraint_name) {
    */
   public function getItemDefinition() {
     if (!isset($this->itemDefinition)) {
-      $this->itemDefinition = DataDefinition::create('field_item:' . $this->type)
+      $this->itemDefinition = FieldItemDataDefinition::create($this)
         ->setSettings($this->getSettings());
     }
     return $this->itemDefinition;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinition($name) {
+    if (!isset($this->propertyDefinitions)) {
+      $this->getPropertyDefinitions();
+    }
+    if (isset($this->propertyDefinitions[$name])) {
+      return $this->propertyDefinitions[$name];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset($this->propertyDefinitions)) {
+      $class = $this->getFieldItemClass();
+      $this->propertyDefinitions = $class::propertyDefinitions($this);
+    }
+    return $this->propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyNames() {
+    return array_keys($this->getPropertyDefinitions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    $class = $this->getFieldItemClass();
+    return $class::getMainPropertyName();
+  }
+
+  /**
+   * Helper to retrieve the field item class.
+   *
+   * @todo: Remove once getClass() adds in defaults. See
+   * https://drupal.org/node/2116341.
+   */
+  protected function getFieldItemClass() {
+    if ($class = $this->getItemDefinition()->getClass()) {
+      return $class;
+    }
+    else {
+      $type_definition = \Drupal::typedDataManager()
+        ->getDefinition($this->getItemDefinition()->getDataType());
+      return $type_definition['class'];
+    }
+  }
+
 }
diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
index 620e48c..ffa87e8 100644
--- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
 use Drupal\field\FieldException;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\field\FieldInstanceInterface;
@@ -492,14 +493,6 @@ public function getSetting($setting_name) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyNames() {
-    $schema = $this->field->getSchema();
-    return array_keys($schema['columns']);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function isTranslatable() {
     return $this->field->translatable;
   }
@@ -698,7 +691,7 @@ public function getConstraint($constraint_name) {
    */
   public function getItemDefinition() {
     if (!isset($this->itemDefinition)) {
-      $this->itemDefinition = DataDefinition::create('field_item:' . $this->field->type)
+      $this->itemDefinition = FieldItemDataDefinition::create($this)
         ->setSettings($this->getSettings());
     }
     return $this->itemDefinition;
@@ -707,6 +700,34 @@ public function getItemDefinition() {
   /**
    * {@inheritdoc}
    */
+  public function getPropertyDefinition($name) {
+    return $this->field->getPropertyDefinition($name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    return $this->field->getPropertyDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyNames() {
+    return $this->field->getPropertyNames();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return $this->field->getMainPropertyName();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getSchema() {
     return $this->field->getSchema();
   }
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php
index d3597fd..700efde 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/HiddenTestItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\field_test\Plugin\Field\FieldType\TestItem;
 
 /**
  * Defines the 'hidden_test' entity field item.
@@ -25,23 +25,13 @@
 class HiddenTestItem extends TestItem {
 
   /**
-   * Property definitions of the contained properties.
-   *
-   * @see TestItem::getPropertyDefinitions()
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  static $propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('integer')
+      ->setLabel(t('Test integer value'));
 
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
-        ->setLabel(t('Test integer value'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
 }
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/ShapeItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/ShapeItem.php
index 15d05cd..63487c1 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/ShapeItem.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/ShapeItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\ConfigFieldItemBase;
 
 /**
@@ -28,26 +28,16 @@
 class ShapeItem extends ConfigFieldItemBase {
 
   /**
-   * Property definitions of the contained properties.
-   *
-   * @see ShapeItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['shape'] = DataDefinition::create('string')
-        ->setLabel(t('Shape'));
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['shape'] = DataDefinition::create('string')
+      ->setLabel(t('Shape'));
 
-      static::$propertyDefinitions['color'] = DataDefinition::create('string')
-        ->setLabel(t('Color'));
-    }
-    return static::$propertyDefinitions;
+    $properties['color'] = DataDefinition::create('string')
+      ->setLabel(t('Color'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/TestItem.php
index be3955f..d2d769a 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Field/FieldType/TestItem.php
@@ -35,23 +35,13 @@
 class TestItem extends ConfigFieldItemBase implements PrepareCacheInterface {
 
   /**
-   * Property definitions of the contained properties.
-   *
-   * @see TestItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
-        ->setLabel(t('Test integer value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('integer')
+      ->setLabel(t('Test integer value'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
index 0be4a99..81c4dff 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
@@ -20,6 +20,7 @@
  *   label = @Translation("File"),
  *   description = @Translation("This field stores the ID of a file as an integer value."),
  *   settings = {
+ *     "target_type" = "file",
  *     "display_field" = "0",
  *     "display_default" = "0",
  *     "uri_scheme" = ""
@@ -38,15 +39,6 @@
 class FileItem extends EntityReferenceItem implements ConfigFieldItemInterface {
 
   /**
-   * Property definitions of the contained properties.
-   *
-   * @see FileItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -87,19 +79,16 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $this->definition->setSetting('target_type', 'file');
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions = parent::getPropertyDefinitions();
+    $properties['display'] = DataDefinition::create('boolean')
+      ->setLabel(t('Flag to control whether this file should be displayed when viewing content'));
 
-      static::$propertyDefinitions['display'] = DataDefinition::create('boolean')
-        ->setLabel(t('Flag to control whether this file should be displayed when viewing content'));
+    $properties['description'] = DataDefinition::create('string')
+      ->setLabel(t('A description of the file'));
 
-      static::$propertyDefinitions['description'] = DataDefinition::create('string')
-        ->setLabel(t('A description of the file'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
index 09987f2..5442d17 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\image\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\file\Plugin\Field\FieldType\FileItem;
 
 /**
@@ -19,6 +19,7 @@
  *   label = @Translation("Image"),
  *   description = @Translation("This field stores the ID of an image file as an integer value."),
  *   settings = {
+ *     "target_type" = "file",
  *     "uri_scheme" = "",
  *     "default_image" = {
  *       "fid" = NULL,
@@ -117,25 +118,22 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $this->definition->setSetting('target_type', 'file');
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
 
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions = parent::getPropertyDefinitions();
+    $properties['alt'] = DataDefinition::create('string')
+      ->setLabel(t("Alternative image text, for the image's 'alt' attribute."));
 
-      static::$propertyDefinitions['alt'] = DataDefinition::create('string')
-        ->setLabel(t("Alternative image text, for the image's 'alt' attribute."));
+    $properties['title'] = DataDefinition::create('string')
+      ->setLabel(t("Image title text, for the image's 'title' attribute."));
 
-      static::$propertyDefinitions['title'] = DataDefinition::create('string')
-        ->setLabel(t("Image title text, for the image's 'title' attribute."));
+    $properties['width'] = DataDefinition::create('integer')
+      ->setLabel(t('The width of the image in pixels.'));
 
-      static::$propertyDefinitions['width'] = DataDefinition::create('integer')
-        ->setLabel(t('The width of the image in pixels.'));
+    $properties['height'] = DataDefinition::create('integer')
+      ->setLabel(t('The height of the image in pixels.'));
 
-      static::$propertyDefinitions['height'] = DataDefinition::create('integer')
-        ->setLabel(t('The height of the image in pixels.'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
index bc28594..17853c3 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
@@ -8,8 +8,9 @@
 namespace Drupal\link\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\ConfigFieldItemBase;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\MapDataDefinition;
 
 /**
  * Plugin implementation of the 'link' field type.
@@ -28,27 +29,19 @@
 class LinkItem extends ConfigFieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['url'] = DataDefinition::create('uri')
-        ->setLabel(t('URL'));
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['url'] = DataDefinition::create('uri')
+      ->setLabel(t('URL'));
+
+    $properties['title'] = DataDefinition::create('string')
+      ->setLabel(t('Link text'));
 
-      static::$propertyDefinitions['title'] = DataDefinition::create('string')
-        ->setLabel(t('Link text'));
+    $properties['attributes'] = MapDataDefinition::create()
+      ->setLabel(t('Attributes'));
 
-      static::$propertyDefinitions['attributes'] = DataDefinition::create('map')
-        ->setLabel(t('Attributes'));
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/DecimalItem.php b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/DecimalItem.php
index 42588e8..bba5591 100644
--- a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/DecimalItem.php
+++ b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/DecimalItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\number\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Component\Utility\MapArray;
 
 /**
@@ -37,12 +37,11 @@ class DecimalItem extends NumberItemBase {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Decimal value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Decimal value'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/FloatItem.php b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/FloatItem.php
index 5765147..9e545c5 100644
--- a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/FloatItem.php
+++ b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/FloatItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\number\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'number_float' field type.
@@ -32,12 +32,11 @@ class FloatItem extends NumberItemBase {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('float')
-        ->setLabel(t('Float value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('float')
+      ->setLabel(t('Float value'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/IntegerItem.php b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/IntegerItem.php
index 6c44d76..e9eecde 100644
--- a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/IntegerItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\number\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'number_integer' field type.
@@ -32,12 +32,11 @@ class IntegerItem extends NumberItemBase {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
-        ->setLabel(t('Integer value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('integer')
+      ->setLabel(t('Integer value'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/NumberItemBase.php b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/NumberItemBase.php
index 22f2d59..c8f29b8 100644
--- a/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/NumberItemBase.php
+++ b/core/modules/number/lib/Drupal/number/Plugin/Field/FieldType/NumberItemBase.php
@@ -15,13 +15,6 @@
 abstract class NumberItemBase extends ConfigFieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public function instanceSettingsForm(array $form, array &$form_state) {
diff --git a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListBooleanItem.php b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListBooleanItem.php
index 558a103..b283d7f 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListBooleanItem.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListBooleanItem.php
@@ -29,13 +29,6 @@
 class ListBooleanItem extends ListItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -52,12 +45,11 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('boolean')
-        ->setLabel(t('Boolean value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('boolean')
+      ->setLabel(t('Boolean value'));
+
+    return $properties;
   }
 
 }
diff --git a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListFloatItem.php b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListFloatItem.php
index f8d0adb..cfd9251 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListFloatItem.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListFloatItem.php
@@ -29,13 +29,6 @@
 class ListFloatItem extends ListItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -52,12 +45,11 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('float')
-        ->setLabel(t('Float value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('float')
+      ->setLabel(t('Float value'));
+
+    return $properties;
   }
 
 }
diff --git a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListIntegerItem.php b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListIntegerItem.php
index 9c6e7d7..ea28a94 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListIntegerItem.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListIntegerItem.php
@@ -29,13 +29,6 @@
 class ListIntegerItem extends ListItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -52,12 +45,11 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
-        ->setLabel(t('Integer value'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('integer')
+      ->setLabel(t('Integer value'));
+
+    return $properties;
   }
 
 }
diff --git a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListTextItem.php b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListTextItem.php
index 1e27eac..11757d2 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListTextItem.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/Field/FieldType/ListTextItem.php
@@ -29,13 +29,6 @@
 class ListTextItem extends ListItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -53,13 +46,13 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      $constraints = array('Length' => array('max' => 255));
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Text value'))
-        ->setConstraints($constraints);
-   }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $constraints = array('Length' => array('max' => 255));
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Text value'))
+      ->setConstraints($constraints);
+
+    return $properties;
   }
+
 }
diff --git a/core/modules/path/lib/Drupal/path/Plugin/Field/FieldType/PathItem.php b/core/modules/path/lib/Drupal/path/Plugin/Field/FieldType/PathItem.php
index 6d75cac..2e5f5d8 100644
--- a/core/modules/path/lib/Drupal/path/Plugin/Field/FieldType/PathItem.php
+++ b/core/modules/path/lib/Drupal/path/Plugin/Field/FieldType/PathItem.php
@@ -24,26 +24,16 @@
 class PathItem extends FieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see PathItem::getPropertyDefinitions()
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  static $propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['alias'] = DataDefinition::create('string')
+      ->setLabel(t('Path alias'));
 
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['alias'] = DataDefinition::create('string')
-        ->setLabel(t('Path alias'));
-
-      static::$propertyDefinitions['pid'] = DataDefinition::create('string')
-        ->setLabel(t('Path id'));
-    }
-    return static::$propertyDefinitions;
+    $properties['pid'] = DataDefinition::create('string')
+      ->setLabel(t('Path id'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index a4a19e2..01cb430 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -313,7 +313,7 @@ function rdf_preprocess_node(&$variables) {
     $comment_count_mapping = $mapping->getPreparedFieldMapping('comment_count');
     if (!empty($comment_count_mapping['properties'])) {
       $fields = array_keys(\Drupal::service('comment.manager')->getFields('node'));
-      $definitions = array_keys($variables['node']->getPropertyDefinitions());
+      $definitions = array_keys($variables['node']->getFieldDefinitions());
       $valid_fields = array_intersect($fields, $definitions);
       $count = 0;
       foreach ($valid_fields as $field_name) {
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathItem.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathItem.php
index 1010000..10dbed5 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathItem.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathItem.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\shortcut;
 
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
 use Drupal\Core\TypedData\DataDefinition;
 
@@ -16,25 +17,14 @@
 class ShortcutPathItem extends StringItem {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @see self::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('String value'))
-        ->setComputed(TRUE)
-        ->setClass('\Drupal\shortcut\ShortcutPathValue');
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('String value'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\shortcut\ShortcutPathValue');
+    return $properties;
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index 2ea38e9..c7c6a19 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -7,12 +7,16 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Core\Entity\TypedData\EntityDataDefinition;
+use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldDefinition;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 
@@ -26,7 +30,7 @@ class EntityFieldTest extends EntityUnitTestBase  {
    *
    * @var array
    */
-  public static $modules = array('filter', 'text', 'node');
+  public static $modules = array('filter', 'text', 'node', 'user');
 
   public static function getInfo() {
     return array(
@@ -360,30 +364,55 @@ protected function checkIntrospection($entity_type) {
     $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
     $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
 
+    // Test deriving further metadata.
+    $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
+    $field_item_definition = $definitions['name']->getItemDefinition();
+    $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
+    $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
+    $value_definition = $field_item_definition->getPropertyDefinition('value');
+    $this->assertTrue($value_definition instanceof DataDefinitionInterface);
+    $this->assertEqual($value_definition->getDataType(), 'string');
+
+    // Test deriving metadata from references.
+    $entity_definition = \Drupal\Core\Entity\TypedData\EntityDataDefinition::create($entity_type);
+    $reference_definition = $entity_definition->getPropertyDefinition('langcode')
+      ->getPropertyDefinition('language')
+      ->getTargetDefinition();
+    $this->assertEqual($reference_definition->getDataType(), 'language');
+
+    $reference_definition = $entity_definition->getPropertyDefinition('user_id')
+      ->getPropertyDefinition('entity')
+      ->getTargetDefinition();
+
+    $this->assertTrue($reference_definition instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
+    $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
+
+    // Test propagating down.
+    $name_definition = $reference_definition->getPropertyDefinition('name');
+    $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
+    $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
+
     // Test introspecting an entity object.
     // @todo: Add bundles and test bundles as well.
     $entity = entity_create($entity_type);
 
-    $definitions = $entity->getPropertyDefinitions();
+    $definitions = $entity->getFieldDefinitions();
     $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
     $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
     $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
 
-    $name_properties = $entity->name->getPropertyDefinitions();
+    $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
     $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type .': String value property of the name found.');
 
-    $userref_properties = $entity->user_id->getPropertyDefinitions();
+    $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
     $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type .': Entity id property of the user found.');
     $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type .': Entity reference property of the user found.');
 
-    $textfield_properties = $entity->field_test_text->getPropertyDefinitions();
+    $textfield_properties = $entity->field_test_text->getFieldDefinition()->getPropertyDefinitions();
     $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type .': String value property of the test-text field found.');
     $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type .': String format field of the test-text field found.');
     $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.');
 
-    // @todo: Once the user entity has definitions, continue testing getting
-    // them from the $userref_values['entity'] property.
-
     // Make sure provided contextual information is right.
     $this->assertIdentical($entity->getRoot(), $entity, 'Entity is root object.');
     $this->assertEqual($entity->getPropertyPath(), '');
@@ -446,7 +475,7 @@ protected function assertIterator($entity_type) {
     }
 
     $properties = $entity->getProperties();
-    $this->assertEqual(array_keys($properties), array_keys($entity->getPropertyDefinitions()), format_string('%entity_type: All properties returned.', array('%entity_type' => $entity_type)));
+    $this->assertEqual(array_keys($properties), array_keys($entity->getDefinition()->getPropertyDefinitions()), format_string('%entity_type: All properties returned.', array('%entity_type' => $entity_type)));
     $this->assertEqual($properties, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all properties.', array('%entity_type' => $entity_type)));
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTypedDataDefinitionTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTypedDataDefinitionTest.php
new file mode 100644
index 0000000..4c3b63e
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTypedDataDefinitionTest.php
@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\TypedData\EntityTypedDataDefinitionTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\Core\Entity\TypedData\EntityDataDefinition;
+use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
+use Drupal\Core\Field\FieldDefinition;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\DataReferenceDefinition;
+use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
+use Drupal\Core\TypedData\ListDataDefinitionInterface;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests deriving metadata of entity and field data types.
+ */
+class EntityTypedDataDefinitionTest extends DrupalUnitTestBase {
+
+  /**
+   * The typed data manager to use.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager
+   */
+  protected $typedDataManager;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('filter', 'text', 'node');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity typed data definitions',
+      'description' => 'Tests reading and deriving metadata of entity and field data types.',
+      'group' => 'Typed Data API',
+    );
+  }
+
+  public function setUp() {
+    parent::setup();
+    $this->typedDataManager = $this->container->get('typed_data_manager');
+  }
+
+  /**
+   * Tests deriving metadata about fields.
+   */
+  public function testFields() {
+    $field_definition = FieldDefinition::create('integer');
+    // Fields are lists of complex data.
+    $this->assertTrue($field_definition instanceof ListDataDefinitionInterface);
+    $this->assertFalse($field_definition instanceof ComplexDataDefinitionInterface);
+    $field_item_definition = $field_definition->getItemDefinition();
+    $this->assertFalse($field_item_definition instanceof ListDataDefinitionInterface);
+    $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
+
+    // Derive metadata about field item properties.
+    $this->assertEqual(array_keys($field_item_definition->getPropertyDefinitions()), array('value'));
+    $this->assertEqual($field_item_definition->getPropertyDefinition('value')->getDataType(), 'integer');
+    $this->assertEqual($field_item_definition->getMainPropertyName(), 'value');
+    $this->assertNull($field_item_definition->getPropertyDefinition('invalid'));
+
+    // To ease access of field item property metadata, field definitions make
+    // metadata about field item properties on the field level as well.
+    $this->assertTrue($field_definition instanceof FieldDefinitionInterface);
+    $this->assertEqual(array_keys($field_definition->getPropertyDefinitions()), array('value'));
+    $this->assertEqual($field_definition->getPropertyDefinition('value')->getDataType(), 'integer');
+    $this->assertEqual($field_definition->getMainPropertyName(), 'value');
+    $this->assertNull($field_definition->getPropertyDefinition('invalid'));
+
+    // Test using the definition factory for field item lists and field items.
+    $field_item2 = $this->typedDataManager->createDataDefinition('field_item:integer');
+    $this->assertFalse($field_item2 instanceof ListDataDefinitionInterface);
+    $this->assertTrue($field_item2 instanceof ComplexDataDefinitionInterface);
+    // Comparison should ignore the internal static cache, so compare the
+    // serialized objects instead.
+    $this->assertEqual(serialize($field_item_definition), serialize($field_item2));
+
+    $field_definition2 = $this->typedDataManager->createDataDefinition('field_item:integer', TRUE);
+    $this->assertTrue($field_definition2 instanceof ListDataDefinitionInterface);
+    $this->assertFalse($field_definition2 instanceof ComplexDataDefinitionInterface);
+    $this->assertEqual(serialize($field_definition), serialize($field_definition2));
+  }
+
+  /**
+   * Tests deriving metadata about entities.
+   */
+  public function testEntities() {
+    $entity_definition = EntityDataDefinition::create('node');
+    // Entities are complex data.
+    $this->assertFalse($entity_definition instanceof ListDataDefinitionInterface);
+    $this->assertTrue($entity_definition instanceof ComplexDataDefinitionInterface);
+
+    $field_definitions = $entity_definition->getPropertyDefinitions();
+    // Comparison should ignore the internal static cache, so compare the
+    // serialized objects instead.
+    $this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getFieldDefinitions('node')));
+    $this->assertEqual($entity_definition->getPropertyDefinition('title')->getItemDefinition()->getDataType(), 'field_item:text');
+    $this->assertNull($entity_definition->getMainPropertyName());
+    $this->assertNull($entity_definition->getPropertyDefinition('invalid'));
+
+    $entity_definition2 = $this->typedDataManager->createDataDefinition('entity:node', FALSE);
+    $this->assertFalse($entity_definition2 instanceof ListDataDefinitionInterface);
+    $this->assertTrue($entity_definition2 instanceof ComplexDataDefinitionInterface);
+    $this->assertEqual(serialize($entity_definition), serialize($entity_definition2));
+
+    // Test that definition factory creates the right definitions for all
+    // entity data types variants.
+    $this->assertEqual($this->typedDataManager->createDataDefinition('entity'), EntityDataDefinition::create());
+    $this->assertEqual($this->typedDataManager->createDataDefinition('entity:node'), EntityDataDefinition::create('node'));
+  }
+
+  /**
+   * Tests deriving metadata from entity references.
+   */
+  public function testEntityReferences() {
+    $reference_definition = DataReferenceDefinition::create('entity');
+    $this->assertTrue($reference_definition instanceof DataReferenceDefinitionInterface);
+
+    // Test retrieving metadata about the referenced data.
+    $this->assertEqual($reference_definition->getTargetDefinition()->getDataType(), 'entity');
+    $this->assertTrue($reference_definition->getTargetDefinition() instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface);
+
+    // Test using the definition factory.
+    $reference_definition2 = $this->typedDataManager->createDataDefinition('entity_reference');
+    $this->assertTrue($reference_definition2 instanceof DataReferenceDefinitionInterface);
+    $this->assertEqual($reference_definition2, $reference_definition);
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataDefinitionTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataDefinitionTest.php
new file mode 100644
index 0000000..4872f06
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataDefinitionTest.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\TypedData\TypedDataDefinitionTest.
+ */
+
+namespace Drupal\system\Tests\TypedData;
+
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\DataReferenceDefinition;
+use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
+use Drupal\Core\TypedData\ListDataDefinition;
+use Drupal\Core\TypedData\ListDataDefinitionInterface;
+use Drupal\Core\TypedData\MapDataDefinition;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests deriving metadata of core data types.
+ */
+class TypedDataDefinitionTest extends DrupalUnitTestBase {
+
+  /**
+   * The typed data manager to use.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager
+   */
+  protected $typedDataManager;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Typed data definitions',
+      'description' => 'Tests reading and deriving metadata of core data types.',
+      'group' => 'Typed Data API',
+    );
+  }
+
+  public function setUp() {
+    parent::setup();
+    $this->typedDataManager = $this->container->get('typed_data_manager');
+  }
+
+  /**
+   * Tests deriving metadata about list items.
+   */
+  public function testLists() {
+    $list_definition = ListDataDefinition::create('string');
+    $this->assertTrue($list_definition instanceof ListDataDefinitionInterface);
+    $item_definition = $list_definition->getItemDefinition();
+    $this->assertTrue($item_definition instanceof DataDefinitionInterface);
+    $this->assertEqual($item_definition->getDataType(), 'string');
+
+    // Test using the definition factory.
+    $list_definition2 = $this->typedDataManager->createDataDefinition('string', TRUE);
+    $this->assertTrue($list_definition2 instanceof ListDataDefinitionInterface);
+    $this->assertEqual($list_definition, $list_definition2);
+
+    // Test creating a single list item, which is the same as a list of any
+    // items.
+    $list_definition = $this->typedDataManager->createDataDefinition('list');
+    $this->assertTrue($list_definition instanceof ListDataDefinitionInterface);
+    $this->assertEqual($list_definition->getDataType(), 'list');
+    $this->assertEqual($list_definition->getItemDefinition()->getDataType(), 'any');
+  }
+
+  /**
+   * Tests deriving metadata about maps.
+   */
+  public function testMaps() {
+    $map_definition = MapDataDefinition::create()
+      ->setPropertyDefinition('one', DataDefinition::create('string'))
+      ->setPropertyDefinition('two', DataDefinition::create('string'))
+      ->setPropertyDefinition('three', DataDefinition::create('string'));
+
+    $this->assertTrue($map_definition instanceof ComplexDataDefinitionInterface);
+
+    // Test retrieving metadata about contained properties.
+    $this->assertEqual(array_keys($map_definition->getPropertyDefinitions()), array('one', 'two', 'three'));
+    $this->assertEqual($map_definition->getPropertyDefinition('one')->getDataType(), 'string');
+    $this->assertNull($map_definition->getMainPropertyName());
+    $this->assertNull($map_definition->getPropertyDefinition('invalid'));
+
+    // Test using the definition factory.
+    $map_definition2 = $this->typedDataManager->createDataDefinition('map');
+    $this->assertTrue($map_definition2 instanceof ComplexDataDefinitionInterface);
+    $map_definition2->setPropertyDefinition('one', DataDefinition::create('string'))
+      ->setPropertyDefinition('two', DataDefinition::create('string'))
+      ->setPropertyDefinition('three', DataDefinition::create('string'));
+    $this->assertEqual($map_definition, $map_definition2);
+  }
+
+  /**
+   * Tests deriving metadata from data references.
+   */
+  public function testDataReferences() {
+    $language_reference_definition = DataReferenceDefinition::create('language');
+    $this->assertTrue($language_reference_definition instanceof DataReferenceDefinitionInterface);
+
+    // Test retrieving metadata about the referenced data.
+    $this->assertEqual($language_reference_definition->getTargetDefinition()->getDataType(), 'language');
+
+    // Test using the definition factory.
+    $language_reference_definition2 = $this->typedDataManager->createDataDefinition('language_reference');
+    $this->assertTrue($language_reference_definition2 instanceof DataReferenceDefinitionInterface);
+    $this->assertEqual($language_reference_definition, $language_reference_definition2);
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
index eeb6bb5..aeb5bf8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -9,7 +9,9 @@
 
 use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\ListDefinition;
+use Drupal\Core\TypedData\ListDataDefinition;
+use Drupal\Core\TypedData\MapDataDefinition;
+use Drupal\Core\TypedData\Plugin\DataType\Map;
 use Drupal\simpletest\DrupalUnitTestBase;
 use Drupal\Core\Datetime\DrupalDateTime;
 
@@ -318,7 +320,7 @@ public function testGetAndSet() {
   public function testTypedDataLists() {
     // Test working with an existing list of strings.
     $value = array('one', 'two', 'three');
-    $typed_data = $this->createTypedData(ListDefinition::create('string'), $value);
+    $typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
     $this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
     // Test iterating.
     $count = 0;
@@ -410,9 +412,12 @@ public function testTypedDataMaps() {
       'two' => 'zwei',
       'three' => 'drei',
     );
-    $typed_data = $this->createTypedData(array(
-      'type' => 'map',
-    ), $value);
+    $definition = MapDataDefinition::create()
+      ->setPropertyDefinition('one', DataDefinition::create('string'))
+      ->setPropertyDefinition('two', DataDefinition::create('string'))
+      ->setPropertyDefinition('three', DataDefinition::create('string'));
+
+    $typed_data = $this->createTypedData($definition, $value);
 
     // Test iterating.
     $count = 0;
@@ -423,10 +428,10 @@ public function testTypedDataMaps() {
     $this->assertEqual($count, 3);
 
     // Test retrieving metadata.
-    $this->assertEqual(array_keys($typed_data->getPropertyDefinitions()), array_keys($value));
-    $definition = $typed_data->getPropertyDefinition('one');
-    $this->assertEqual($definition->getDataType(), 'any');
-    $this->assertFalse($typed_data->getPropertyDefinition('invalid'));
+    $this->assertEqual(array_keys($typed_data->getDefinition()->getPropertyDefinitions()), array_keys($value));
+    $definition = $typed_data->getDefinition()->getPropertyDefinition('one');
+    $this->assertEqual($definition->getDataType(), 'string');
+    $this->assertNull($typed_data->getDefinition()->getPropertyDefinition('invalid'));
 
     // Test getting and setting properties.
     $this->assertEqual($typed_data->get('one')->getValue(), 'eins');
@@ -450,8 +455,11 @@ public function testTypedDataMaps() {
     $this->assertEqual($typed_data->get('two')->getValue(), 'zwei');
     $this->assertEqual($typed_data->get('three')->getValue(), 'drei');
 
+    // Test setting a not defined property. It shouldn't show up in the
+    // properties, but be kept in the values.
     $typed_data->setValue(array('foo' => 'bar'));
-    $this->assertEqual(array_keys($typed_data->getProperties()), array('foo'));
+    $this->assertEqual(array_keys($typed_data->getProperties()), array('one', 'two', 'three'));
+    $this->assertEqual(array_keys($typed_data->getValue()), array('foo', 'one', 'two', 'three'));
 
     // Test getting the string representation.
     $typed_data->setValue(array('one' => 'eins', 'two' => '', 'three' => 'drei'));
@@ -492,10 +500,11 @@ public function testTypedDataMaps() {
       $this->pass('Exception thrown:' . $e->getMessage());
     }
 
-    // Test adding a new entry to the map.
+    // Test adding a new property to the map.
+    $typed_data->getDefinition()->setPropertyDefinition('zero', DataDefinition::create('any'));
     $typed_data->set('zero', 'null');
     $this->assertEqual($typed_data->get('zero')->getValue(), 'null');
-    $definition = $typed_data->getPropertyDefinition('zero');
+    $definition = $typed_data->get('zero')->getDefinition();
     $this->assertEqual($definition->getDataType(), 'any', 'Definition for a new map entry returned.');
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Validation/ComplexDataConstraintValidatorTest.php b/core/modules/system/lib/Drupal/system/Tests/Validation/ComplexDataConstraintValidatorTest.php
index 2b0af2f..baabdb8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Validation/ComplexDataConstraintValidatorTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Validation/ComplexDataConstraintValidatorTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Validation;
 
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\MapDataDefinition;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -42,7 +43,8 @@ public function setUp() {
    */
   public function testValidation() {
     // Create a definition that specifies some ComplexData constraint.
-    $definition = DataDefinition::create('map')
+    $definition = MapDataDefinition::create()
+      ->setPropertyDefinition('key', DataDefinition::create('integer'))
       ->addConstraint('ComplexData', array(
         'key' => array(
           'AllowedValues' => array(1, 2, 3)
@@ -71,7 +73,8 @@ public function testValidation() {
     $violations = $typed_data->validate();
     $this->assertEqual($violations->count(), 0, 'Constraint on non-existing key is ignored.');
 
-    $definition = DataDefinition::create('map')
+    $definition = MapDataDefinition::create()
+      ->setPropertyDefinition('key', DataDefinition::create('integer'))
       ->addConstraint('ComplexData', array(
         'key' => array(
           'NotNull' => array()
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
index 2733515..db80f4b 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
@@ -21,6 +21,7 @@
  *   label = @Translation("Term Reference"),
  *   description = @Translation("This field stores a reference to a taxonomy term."),
  *   settings = {
+ *     "target_type" = "taxonomy_term",
  *     "options_list_callback" = NULL,
  *     "allowed_values" = {
  *       {
@@ -89,14 +90,6 @@ public function getSettableOptions(AccountInterface $account = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    $this->definition['settings']['target_type'] = 'taxonomy_term';
-    return parent::getPropertyDefinitions();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public static function schema(FieldDefinitionInterface $field_definition) {
     return array(
       'columns' => array(
diff --git a/core/modules/telephone/lib/Drupal/telephone/Plugin/Field/FieldType/TelephoneItem.php b/core/modules/telephone/lib/Drupal/telephone/Plugin/Field/FieldType/TelephoneItem.php
index 67d7bdf..e710ded 100644
--- a/core/modules/telephone/lib/Drupal/telephone/Plugin/Field/FieldType/TelephoneItem.php
+++ b/core/modules/telephone/lib/Drupal/telephone/Plugin/Field/FieldType/TelephoneItem.php
@@ -8,8 +8,8 @@
 namespace Drupal\telephone\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\ConfigFieldItemBase;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'telephone' field type.
@@ -25,13 +25,6 @@
 class TelephoneItem extends ConfigFieldItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
   public static function schema(FieldDefinitionInterface $field_definition) {
@@ -49,12 +42,11 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Telephone number'));
-    }
-    return static::$propertyDefinitions;
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Telephone number'));
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
index 158c416..340e98b 100644
--- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
+++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\text\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\ConfigFieldItemBase;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\Core\TypedData\DataDefinition;
 
@@ -17,31 +18,23 @@
 abstract class TextItemBase extends ConfigFieldItemBase implements PrepareCacheInterface {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = DataDefinition::create('string')
-        ->setLabel(t('Text value'));
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Text value'));
 
-      static::$propertyDefinitions['format'] = DataDefinition::create('filter_format')
-        ->setLabel(t('Text format'));
+    $properties['format'] = DataDefinition::create('filter_format')
+      ->setLabel(t('Text format'));
 
-      static::$propertyDefinitions['processed'] = DataDefinition::create('string')
-        ->setLabel(t('Processed text'))
-        ->setDescription(t('The text value with the text format applied.'))
-        ->setComputed(TRUE)
-        ->setClass('\Drupal\text\TextProcessed')
-        ->setSetting('text source', 'value');
-    }
-    return static::$propertyDefinitions;
+    $properties['processed'] = DataDefinition::create('string')
+      ->setLabel(t('Processed text'))
+      ->setDescription(t('The text value with the text format applied.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\text\TextProcessed')
+      ->setSetting('text source', 'value');
+
+    return $properties;
   }
 
   /**
diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextWithSummaryItem.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextWithSummaryItem.php
index 3942510..5fe30fc 100644
--- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextWithSummaryItem.php
+++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextWithSummaryItem.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\text\Plugin\Field\FieldType;
 
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'text_with_summary' field type.
@@ -28,30 +28,22 @@
 class TextWithSummaryItem extends TextItemBase {
 
   /**
-   * Definitions of the contained properties.
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
    * {@inheritdoc}
    */
-  public function getPropertyDefinitions() {
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions = parent::getPropertyDefinitions();
+  public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
+
+    $properties['summary'] = DataDefinition::create('string')
+      ->setLabel(t('Summary text value'));
 
-      static::$propertyDefinitions['summary'] = DataDefinition::create('string')
-        ->setLabel(t('Summary text value'));
+    $properties['summary_processed'] = DataDefinition::create('string')
+      ->setLabel(t('Processed summary text'))
+      ->setDescription(t('The summary text value with the text format applied.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\text\TextProcessed')
+      ->setSetting('text source', 'summary');
 
-      static::$propertyDefinitions['summary_processed'] = DataDefinition::create('string')
-        ->setLabel(t('Processed summary text'))
-        ->setDescription(t('The summary text value with the text format applied.'))
-        ->setComputed(TRUE)
-        ->setClass('\Drupal\text\TextProcessed')
-        ->setSetting('text source', 'summary');
-    }
-    return static::$propertyDefinitions;
+    return $properties;
   }
 
   /**
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php
index 8b54b67..754a08f 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php
@@ -112,7 +112,7 @@ function testValidation() {
     $violations = $user->validate();
     // @todo There are two violations because EmailItem::getConstraints()
     //   overlaps with the implicit constraint of the 'email' property type used
-    //   in EmailItem::getPropertyDefinitions(). Resolve this in
+    //   in EmailItem::propertyDefinitions(). Resolve this in
     //   https://drupal.org/node/2023465.
     $this->assertEqual(count($violations), 2, 'Violations found when email is too long');
     $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 96d5970..e96b9b9 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -541,23 +541,6 @@ public function testGetFieldDefinitionsInvalidDefinition() {
   }
 
   /**
-   * Tests the getFieldDefinitionsByConstraints() method.
-   *
-   * @covers ::getFieldDefinitionsByConstraints()
-   */
-  public function testGetFieldDefinitionsByConstraints() {
-    $field_definition = $this->setUpEntityWithFieldDefinition();
-
-    $this->moduleHandler->expects($this->exactly(2))
-      ->method('invokeAll')
-      ->will($this->returnValue(array()));
-
-    $expected = array('id' => $field_definition);
-    $this->assertSame($expected, $this->entityManager->getFieldDefinitionsByConstraints('test_entity_type', array()));
-    $this->assertSame($expected, $this->entityManager->getFieldDefinitionsByConstraints('test_entity_type', array('Bundle' => 'test_entity_bundle')));
-  }
-
-  /**
    * Prepares an entity that defines a field definition.
    *
    * @param bool $custom_invoke_all
diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
index 39b73cf..c90ed93 100644
--- a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
@@ -84,7 +84,7 @@ public function setUp() {
    * Tests field name methods.
    */
   public function testFieldName() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $field_name = $this->randomName();
     $definition->setName($field_name);
     $this->assertEquals($field_name, $definition->getName());
@@ -94,7 +94,7 @@ public function testFieldName() {
    * Tests field label methods.
    */
   public function testFieldLabel() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $label = $this->randomName();
     $definition->setLabel($label);
     $this->assertEquals($label, $definition->getLabel());
@@ -104,7 +104,7 @@ public function testFieldLabel() {
    * Tests field description methods.
    */
   public function testFieldDescription() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $description = $this->randomName();
     $definition->setDescription($description);
     $this->assertEquals($description, $definition->getDescription());
@@ -122,12 +122,13 @@ public function testFieldType() {
    * Tests field settings methods.
    */
   public function testFieldSettings() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $setting = $this->randomName();
     $value = $this->randomName();
     $definition->setSetting($setting, $value);
     $this->assertEquals($value, $definition->getSetting($setting));
-    $this->assertEquals(array($setting => $value), $definition->getSettings());
+    $default_settings = $this->fieldTypeDefinition['settings'] + $this->fieldTypeDefinition['instance_settings'];
+    $this->assertEquals(array($setting => $value) + $default_settings, $definition->getSettings());
   }
 
   /**
@@ -146,7 +147,7 @@ public function testDefaultFieldSettings() {
    * Tests field default value.
    */
   public function testFieldDefaultValue() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $setting = 'default_value';
     $value = $this->randomName();
     $definition->setSetting($setting, $value);
@@ -160,7 +161,7 @@ public function testFieldDefaultValue() {
    * Tests field translatable methods.
    */
   public function testFieldTranslatable() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $this->assertFalse($definition->isTranslatable());
     $definition->setTranslatable(TRUE);
     $this->assertTrue($definition->isTranslatable());
@@ -172,7 +173,7 @@ public function testFieldTranslatable() {
    * Tests field cardinality.
    */
   public function testFieldCardinality() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $this->assertEquals(1, $definition->getCardinality());
     // @todo: Add more tests when this can be controlled.
   }
@@ -181,7 +182,7 @@ public function testFieldCardinality() {
    * Tests required.
    */
   public function testFieldRequired() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $this->assertFalse($definition->isRequired());
     $definition->setRequired(TRUE);
     $this->assertTrue($definition->isRequired());
@@ -193,7 +194,7 @@ public function testFieldRequired() {
    * Tests configurable.
    */
   public function testFieldConfigurable() {
-    $definition = new FieldDefinition();
+    $definition = FieldDefinition::create($this->fieldType);
     $this->assertFalse($definition->isConfigurable());
   }
 
