diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 6861ca4..e540cac 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -392,7 +392,7 @@ protected function getTranslatedField($property_name, $langcode) {
       }
       // Non-translatable fields are always stored with
       // Language::LANGCODE_DEFAULT as key.
-      if ($langcode != Language::LANGCODE_DEFAULT && empty($definition['translatable'])) {
+      if ($langcode != Language::LANGCODE_DEFAULT && !$definition->isFieldTranslatable()) {
         if (!isset($this->fields[$property_name][Language::LANGCODE_DEFAULT])) {
           $this->fields[$property_name][Language::LANGCODE_DEFAULT] = $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT);
         }
@@ -424,7 +424,7 @@ public function set($property_name, $value, $notify = TRUE) {
   public function getProperties($include_computed = FALSE) {
     $properties = array();
     foreach ($this->getPropertyDefinitions() as $name => $definition) {
-      if ($include_computed || empty($definition['computed'])) {
+      if ($include_computed || !$definition->isComputed()) {
         $properties[$name] = $this->get($name);
       }
     }
@@ -696,7 +696,7 @@ public function addTranslation($langcode, array $values = array()) {
     $definitions = $translation->getPropertyDefinitions();
 
     foreach ($values as $name => $value) {
-      if (isset($definitions[$name]) && !empty($definitions[$name]['translatable'])) {
+      if (isset($definitions[$name]) && $definitions[$name]->isFieldTranslatable()) {
         $translation->$name = $value;
       }
     }
@@ -710,7 +710,7 @@ public function addTranslation($langcode, array $values = array()) {
   public function removeTranslation($langcode) {
     if (isset($this->translations[$langcode]) && $langcode != Language::LANGCODE_DEFAULT && $langcode != $this->getDefaultLanguage()->id) {
       foreach ($this->getPropertyDefinitions() as $name => $definition) {
-        if (!empty($definition['translatable'])) {
+        if ($definition->isFieldTranslatable()) {
           unset($this->values[$name][$langcode]);
           unset($this->fields[$name][$langcode]);
         }
@@ -765,7 +765,7 @@ public function updateOriginalValues() {
       return;
     }
     foreach ($this->getPropertyDefinitions() as $name => $definition) {
-      if (empty($definition['computed']) && !empty($this->fields[$name])) {
+      if (!$definition->isComputed() && !empty($this->fields[$name])) {
         foreach ($this->fields[$name] as $langcode => $field) {
           $field->filterEmptyValues();
           $this->values[$name][$langcode] = $field->getValue();
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 90bf5d1..b2dee6b 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\Field\FieldDefinition;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManager;
 use Drupal\Core\Language\Language;
@@ -456,15 +457,8 @@ public function getAdminRouteInfo($entity_type, $bundle) {
    *   is passed, no bundle-specific fields are included. Defaults to NULL.
    *
    * @return array
-   *   An array of field definitions of entity fields, keyed by field
-   *   name. In addition to the typed data definition keys as described at
-   *   \Drupal\Core\TypedData\TypedDataManager::create() the following keys are
-   *   supported:
-   *   - queryable: Whether the field is queryable via QueryInterface.
-   *     Defaults to TRUE if 'computed' is FALSE or not set, to FALSE otherwise.
-   *   - translatable: Whether the field is translatable. Defaults to FALSE.
-   *   - configurable: A boolean indicating whether the field is configurable
-   *     via field.module. Defaults to FALSE.
+   *   An array of entity field definitions, keyed by field name. See
+   *   \Drupal\Core\Entity\Field\FieldDefinitionInterface.
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
    * @see \Drupal\Core\Entity\EntityManager::getFieldDefinitionsByConstraints()
@@ -477,6 +471,7 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
         $this->entityFieldInfo[$entity_type] = $cache->data;
       }
       else {
+        // @todo: Refactor to allow for per-bundle overrides.
         $class = $this->factory->getPluginClass($entity_type, $this->getDefinition($entity_type));
         $this->entityFieldInfo[$entity_type] = array(
           'definitions' => $class::baseFieldDefinitions($entity_type),
@@ -496,12 +491,24 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
         $hooks = array('entity_field_info', $entity_type . '_field_info');
         $this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type], $entity_type);
 
-        // Enforce fields to be multiple by default.
-        foreach ($this->entityFieldInfo[$entity_type]['definitions'] as &$definition) {
-          $definition['list'] = TRUE;
+        // Enforce field definitions to be objects.
+        foreach ($this->entityFieldInfo[$entity_type]['definitions'] as $field_name => &$definition) {
+          if (is_array($definition)) {
+            $definition = FieldDefinition::createFromOldStyleDefinition($definition);
+          }
+          // For manually defined fields, make sure the field name is set.
+          if ($definition instanceof FieldDefinition) {
+            $definition->setFieldName($field_name);
+          }
         }
-        foreach ($this->entityFieldInfo[$entity_type]['optional'] as &$definition) {
-          $definition['list'] = TRUE;
+        foreach ($this->entityFieldInfo[$entity_type]['optional'] as $field_name => &$definition) {
+          if (is_array($definition)) {
+            $definition = FieldDefinition::createFromOldStyleDefinition($definition);
+          }
+          // For manually defined fields, make sure the field name is set.
+          if ($definition instanceof FieldDefinition) {
+            $definition->setFieldName($field_name);
+          }
         }
         $this->cache->set($cid, $this->entityFieldInfo[$entity_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE, 'entity_field_info' => TRUE));
       }
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
index ef79967..dd9ef34 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
@@ -6,34 +6,31 @@
  */
 
 namespace Drupal\Core\Entity\Field;
+
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\ListDefinition;
 
 /**
  * A class for defining entity fields.
  */
-class FieldDefinition implements FieldDefinitionInterface {
-
-  /**
-   * The array holding values for all definition keys.
-   *
-   * @var array
-   */
-  protected $definition = array();
+class FieldDefinition extends ListDefinition implements FieldDefinitionInterface, \ArrayAccess {
 
   /**
-   * Constructs a new FieldDefinition object.
+   * Creates a new entity field definition.
    *
-   * @param array $definition
-   *   (optional) If given, a definition represented as array.
+   * @return \Drupal\Core\Entity\Field\FieldDefinition
    */
-  public function __construct(array $definition = array()) {
-    $this->definition = $definition;
+  public static function create() {
+    $class = get_called_class();
+    return new $class();
   }
 
   /**
    * {@inheritdoc}
    */
   public function getFieldName() {
+    // @todo: Should "name" be part of the data definition also?
     return $this->definition['field_name'];
   }
 
@@ -55,9 +52,18 @@ public function setFieldName($name) {
    * {@inheritdoc}
    */
   public function getFieldType() {
-    // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
-    $parts = explode(':', $this->definition['type']);
-    return $parts[1];
+    // We have to support 'field_item:FIELD_TYPE' data types as well as
+    // 'FIELD_TYPE_field' for field types that are not yet implemented as field
+    // type plugin.
+    $data_type = $this->getItemDefinition()->getDataType();
+    if (strpos($data_type, ':') !== FALSE) {
+      // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
+      $parts = explode(':', $data_type);
+      return $parts[1];
+    }
+    else {
+      return str_replace('_field', '', $data_type);
+    }
   }
 
   /**
@@ -70,43 +76,68 @@ public function getFieldType() {
    *   The object itself for chaining.
    */
   public function setFieldType($type) {
-    $this->definition['type'] = 'field_item:' . $type;
+    // Determine the right data type to use.
+    if (\Drupal::typedData()->getDefinition('field_item:' . $type)) {
+      $this->getItemDefinition()->setDataType('field_item:' . $type);
+    }
+    else {
+      // Data type has not been converted to a field type plugin yet.
+      $this->getItemDefinition()->setDataType($type . '_field');
+    }
     return $this;
   }
 
   /**
-   * Sets a field setting.
+   * {@inheritdoc}
+   */
+  public function getFieldSettings() {
+    return $this->getItemDefinition()->getSettings();
+  }
+
+  /**
+   * Sets field settings.
    *
-   * @param string $type
-   *   The field type to set.
+   * @param array $settings
+   *   The value to set.
    *
    * @return \Drupal\Core\Entity\Field\FieldDefinition
    *   The object itself for chaining.
    */
-  public function setFieldSetting($setting_name, $value) {
-    $this->definition['settings'][$setting_name] = $value;
+  public function setFieldSettings(array $settings) {
+    $this->getItemDefinition()->setSettings($settings);
     return $this;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getFieldSettings() {
-    return $this->definition['settings'];
+  public function getFieldSetting($setting_name) {
+    $settings = $this->getFieldSettings();
+    return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
   }
 
   /**
-   * {@inheritdoc}
+   * Sets a field setting.
+   *
+   * @param string $setting_name
+   *   The field setting to set.
+   * @param mixed $value
+   *   The value to set.
+   *
+   * @return \Drupal\Core\Entity\Field\FieldDefinition
+   *   The object itself for chaining.
    */
-  public function getFieldSetting($setting_name) {
-    return isset($this->definition['settings'][$setting_name]) ? $this->definition['settings'][$setting_name] : NULL;
+  public function setFieldSetting($setting_name, $value) {
+    $settings = $this->getFieldSettings();
+    $settings[$setting_name] = $value;
+    return $this->setFieldSettings($settings);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getFieldPropertyNames() {
-    return array_keys(\Drupal::typedData()->create($this->definition['type'])->getPropertyDefinitions());
+    return array_keys(\Drupal::typedData()->create($this->getItemDefinition())->getPropertyDefinitions());
   }
 
   /**
@@ -134,42 +165,28 @@ public function setTranslatable($translatable) {
    * {@inheritdoc}
    */
   public function getFieldLabel() {
-    return $this->definition['label'];
+    return $this->getLabel();
   }
 
   /**
-   * Sets the field label.
-   *
-   * @param string $label
-   *   The field label to set.
-   *
-   * @return \Drupal\Core\Entity\Field\FieldDefinition
-   *   The object itself for chaining.
+   * {@inheritdoc}
    */
   public function setFieldLabel($label) {
-    $this->definition['label'] = $label;
-    return $this;
+    return $this->setLabel($label);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getFieldDescription() {
-    return $this->definition['description'];
+    return $this->getDescription();
   }
 
   /**
-   * Sets the field label.
-   *
-   * @param string $description
-   *   The field label to set.
-   *
-   * @return \Drupal\Core\Entity\Field\FieldDefinition
-   *   The object itself for chaining.
+   * {@inheritdoc}
    */
   public function setFieldDescription($description) {
-    $this->definition['description'] = $description;
-    return $this;
+    return $this->setDescription($description);
   }
 
   /**
@@ -184,21 +201,40 @@ public function getFieldCardinality() {
    * {@inheritdoc}
    */
   public function isFieldRequired() {
-    return !empty($this->definition['required']);
+    return $this->isRequired();
   }
 
   /**
    * Sets whether the field is required.
    *
-   * @param bool $required
-   *   TRUE if the field is required, FALSE otherwise.
+   * @param boolean $required
+   *   Whether the field is required.
    *
    * @return \Drupal\Core\Entity\Field\FieldDefinition
    *   The object itself for chaining.
    */
   public function setFieldRequired($required) {
-    $this->definition['required'] = $required;
-    return $this;
+    return $this->setRequired($required);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return isset($this->definition['queryable']) ? $this->definition['queryable'] : !$this->isComputed();
+  }
+
+  /**
+   * Sets whether the field is queryable.
+   *
+   * @param boolean $queryable
+   *   Whether the field is queryable.
+   *
+   * @return \Drupal\Core\Entity\Field\FieldDefinition
+   *   The object itself for chaining.
+   */
+  public function setFieldQueryable($queryable) {
+    return $this->setRequired($queryable);
   }
 
   /**
@@ -213,7 +249,9 @@ public function setFieldRequired($required) {
    *   The object itself for chaining.
    */
   public function setPropertyConstraints($name, array $constraints) {
-    $this->definition['item_definition']['constraints']['ComplexData'][$name] = $constraints;
+    $item_constraints = $this->getItemDefinition()->getConstraints();
+    $item_constraints['ComplexData'][$name] = $constraints;
+    $this->getItemDefinition()->setConstraints($item_constraints);
     return $this;
   }
 
@@ -231,4 +269,73 @@ public function getFieldDefaultValue(EntityInterface $entity) {
     return $this->getFieldSetting('default_value');
   }
 
+  /**
+   * Allows creating field definition objects from old style definition arrays.
+   *
+   * @todo: Remove once no old-style definition arrays need to be supported.
+   */
+  public static function createFromOldStyleDefinition(array $definition) {
+    unset($definition['list']);
+
+    // Separate the list item definition from the list definition.
+    $list_definition = $definition;
+    unset($list_definition['type']);
+
+    // Constraints, class and settings apply to the list item.
+    unset($list_definition['constraints']);
+    unset($list_definition['class']);
+    unset($list_definition['settings']);
+
+    $field_definition = new FieldDefinition($list_definition);
+    if (isset($definition['list_class'])) {
+      $field_definition->setClass($definition['list_class']);
+    }
+    else {
+      $type_definition = \Drupal::typedData()->getDefinition($definition['type']);
+      if (isset($type_definition['list_class'])) {
+        $field_definition->setClass($type_definition['list_class']);
+      }
+    }
+
+    // Take care of the item definition now. 'required' applies to the list
+    // definition.
+    unset($definition['required']);
+    $item_definition = new DataDefinition($definition);
+    if (isset($definition['settings'])) {
+      $item_definition->setSettings($definition['settings']);
+    }
+    if (isset($definition['constraints'])) {
+      $item_definition->setConstraints($definition['constraints']);
+    }
+    $field_definition->setItemDefinition($item_definition);
+    return $field_definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function &offsetGet($offset) {
+    if ($offset == 'type') {
+      // What previously was "type" is now the type of the list item.
+      $type = &$this->itemDefinition->offsetGet('type');
+      return $type;
+    }
+    if (!isset($this->definition[$offset])) {
+      $this->definition[$offset] = NULL;
+    }
+    return $this->definition[$offset];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetSet($offset, $value) {
+    if ($offset == 'type') {
+      // What previously was "type" is now the type of the list item.
+      $this->itemDefinition->setDataType($value);
+    }
+    else {
+      $this->definition[$offset] = $value;
+    }
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
index b8dc354..c339576 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
@@ -8,14 +8,16 @@
 namespace Drupal\Core\Entity\Field;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\ListDefinitionInterface;
 
 /**
  * Defines an interface for entity field definitions.
  *
  * An entity field is a data object that holds the values of a particular field
- * for a particular entity (see \Drupal\Core\Entity\Field\FieldItemListInterface). For
- * example, $node_1->body and $node_2->body contain different data and therefore
- * are different field objects.
+ * for a particular entity (see
+ * \Drupal\Core\Entity\Field\FieldItemListInterface). For example, $node_1->body
+ * and $node_2->body contain different data and therefore are different field
+ * objects.
  *
  * In contrast, an entity field *definition* is an object that returns
  * information *about* a field (e.g., its type and settings) rather than its
@@ -36,10 +38,9 @@
  *
  * However, entity base fields, such as $node->title, are not managed by
  * field.module and its "field_entity"/"field_instance" configuration entities.
- * Therefore, their definitions are provided by different objects that implement
- * this interface.
- * @todo That is still in progress: https://drupal.org/node/1949932. Update this
- *   documentation with details when that's implemented.
+ * Therefore, their definitions are provided by different objects based on the
+ * class \Drupal\Core\Entity\Field\FieldDefinition, which implements this
+ * interface as well.
  *
  * Field definitions may fully define a concrete data object (e.g.,
  * $node_1->body), or may provide a best-guess definition for a data object that
@@ -51,7 +52,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 {
+interface FieldDefinitionInterface extends ListDefinitionInterface {
 
   /**
    * Returns the machine name of the field.
@@ -129,6 +130,13 @@ public function isFieldTranslatable();
   public function isFieldConfigurable();
 
   /**
+   * Determines whether the field is queryable via QueryInterface.
+   *
+   * @return bool
+   */
+  public function isFieldQueryable();
+
+  /**
    * Returns the human-readable label for the field.
    *
    * @return string
@@ -143,8 +151,8 @@ public function getFieldLabel();
    * descriptive information is helpful. For example, as help text below the
    * form element in entity edit forms.
    *
-   * @return string
-   *   The field description.
+   * @return string|null
+   *   The field description, or NULL if no description is available.
    */
   public function getFieldDescription();
 
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
index 15744c0..d459c49 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
@@ -8,6 +8,8 @@
 namespace Drupal\Core\Entity\Field;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\Plugin\DataType\Map;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\user;
@@ -23,9 +25,16 @@
 abstract class FieldItemBase extends Map implements FieldItemInterface {
 
   /**
+   * The field's definition.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldDefinitionInterface
+   */
+  protected $definition;
+
+  /**
    * Overrides \Drupal\Core\TypedData\TypedData::__construct().
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
     // Initialize computed properties by default, such that they get cloned
     // with the whole item.
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index 3457acb..7a18f3f 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -263,9 +263,7 @@ public function onBundleDelete($bundle) { }
    */
   public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
     if ($values = $this->readFieldItemsToPurge($entity, $instance)) {
-      $field = $instance->getField();
-      $definition = _field_generate_entity_field_definition($field, $instance);
-      $items = \Drupal::typedData()->create($definition, $values, $field->getFieldName(), $entity);
+      $items = \Drupal::typedData()->create($instance, $values, $instance->getFieldName(), $entity);
       $items->delete();
     }
     $this->purgeFieldItems($entity, $instance);
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/BooleanItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/BooleanItem.php
index 31e3ec8..d1357be 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/BooleanItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/BooleanItem.php
@@ -18,7 +18,7 @@
  *   id = "boolean_field",
  *   label = @Translation("Boolean field item"),
  *   description = @Translation("An entity field containing a boolean value."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList"
  * )
  */
 class BooleanItem extends FieldItemBase {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/DateItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/DateItem.php
index e6f462f..a23233e 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/DateItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/DateItem.php
@@ -18,7 +18,7 @@
  *   id = "date_field",
  *   label = @Translation("Date field item"),
  *   description = @Translation("An entity field containing a date value."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList"
  * )
  */
 class DateItem extends FieldItemBase {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EmailItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EmailItem.php
index 6d1dba6..d736529 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EmailItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EmailItem.php
@@ -19,7 +19,7 @@
  *   id = "email_field",
  *   label = @Translation("E-mail field item"),
  *   description = @Translation("An entity field containing an e-mail value."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList"
  * )
  */
 class EmailItem extends LegacyConfigFieldItem {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php
index 5654747..e8b2980 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php
@@ -25,7 +25,7 @@
  *   id = "entity_reference_field",
  *   label = @Translation("Entity reference field item"),
  *   description = @Translation("An entity field containing an entity reference."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList",
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList",
  *   constraints = {"ValidReference" = TRUE}
  * )
  */
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php
index b630fc7..c338151 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php
@@ -19,7 +19,7 @@
  * @DataType(
  *   id = "field_item",
  *   label = @Translation("Field item"),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList",
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList",
  *   derivative = "Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver"
  * )
  */
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemList.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php
similarity index 76%
rename from core/lib/Drupal/Core/Entity/Field/FieldItemList.php
rename to core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php
index 4b4e1eb..5e6d7dc 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItemList.php
@@ -2,28 +2,30 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Field\FieldItemList.
+ * Contains \Drupal\Core\Entity\Plugin\DataType\FieldItemList.
  */
 
-namespace Drupal\Core\Entity\Field;
+namespace Drupal\Core\Entity\Plugin\DataType;
 
 use Drupal\Core\Entity\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\Core\TypedData\ItemList;
+use Drupal\Core\TypedData\Plugin\DataType\ItemList;
 use Drupal\Core\Language\Language;
 
 /**
- * Represents an entity field; that is, a list of field item objects.
+ * Defines a data type plugin for entity fields, i.e. the list of field items.
  *
- * An entity field is a list of field items, which contain only primitive
- * properties or entity references. Note that even single-valued entity
- * fields are represented as list of items, however for easy access to the
- * contained item the entity field delegates __get() and __set() calls
- * directly to the first item.
+ * An entity field is a list of field items, containing various field item
+ * properties. Note that even single-valued entity fields are represented as
+ * list of field items, however for easy access to the contained item the entity
+ * field delegates __get() and __set() calls directly to the first item.
  *
- * Supported settings (below the definition's 'settings' key) are:
- * - default_value: (optional) If set, the default value to apply to the field.
+ * @DataType(
+ *   id = "field_item_list",
+ *   label = @Translation("Entity field"),
+ *   class = "\Drupal\Core\Entity\Field\Field"
+ * )
  */
 class FieldItemList extends ItemList implements FieldItemListInterface {
 
@@ -45,9 +47,8 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
   /**
    * Overrides TypedData::__construct().
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
-    $this->definition['field_name'] = $name;
     // Always initialize one empty item as most times a value for at least one
     // item will be present. That way prototypes created by
     // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
@@ -80,7 +81,7 @@ public function getLangcode() {
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
-    return new FieldDefinition($this->definition);
+    return $this->definition;
   }
 
   /**
@@ -109,7 +110,7 @@ public function getValue($include_computed = FALSE) {
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\ItemList::setValue().
+   * {@inheritdoc}
    */
   public function setValue($values, $notify = TRUE) {
     if (!isset($values) || $values === array()) {
@@ -214,10 +215,8 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N
    * {@inheritdoc}
    */
   public function applyDefaultValue($notify = TRUE) {
-    // @todo Remove getDefaultValue() and directly call
-    // FieldDefinition::getFieldDefaultValue() here, once
-    // https://drupal.org/node/2047229 is fixed.
-    $value = $this->getDefaultValue();
+    $value = $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
+
     // NULL or array() mean "no default value", but  0, '0' and the empty string
     // are valid default values.
     if (!isset($value) || (is_array($value) && empty($value))) {
@@ -231,33 +230,6 @@ public function applyDefaultValue($notify = TRUE) {
   }
 
   /**
-   * Returns the default value for the field.
-   *
-   * @return array
-   *   The default value for the field.
-   */
-  protected function getDefaultValue() {
-    if (isset($this->definition['settings']['default_value'])) {
-      return $this->definition['settings']['default_value'];
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConstraints() {
-    // Constraints usually apply to the field item, but required does make
-    // sense on the field only. So we special-case it to apply to the field for
-    // now.
-    // @todo: Separate list and list item definitions to separate constraints.
-    $constraints = array();
-    if (!empty($this->definition['required'])) {
-      $constraints[] = \Drupal::typedData()->getValidationConstraintManager()->create('NotNull', array());
-    }
-    return $constraints;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function preSave() {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/IntegerItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/IntegerItem.php
index 6bd8cf8..25c794c 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/IntegerItem.php
@@ -10,6 +10,7 @@
 use Drupal\Core\TypedData\Annotation\DataType;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Entity\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'integer_field' entity field item.
@@ -18,7 +19,7 @@
  *   id = "integer_field",
  *   label = @Translation("Integer field item"),
  *   description = @Translation("An entity field containing an integer value."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_type = "field_item_list"
  * )
  */
 class IntegerItem extends FieldItemBase {
@@ -38,10 +39,9 @@ class IntegerItem extends FieldItemBase {
   public function getPropertyDefinitions() {
 
     if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'integer',
-        'label' => t('Integer value'),
-      );
+      static::$propertyDefinitions['value'] = DataDefinition::create()
+        ->setDataType('integer')
+        ->setLabel(t('Integer value'));
     }
     return static::$propertyDefinitions;
   }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php
index b156a34..42f12a1 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php
@@ -19,7 +19,7 @@
  *   id = "language_field",
  *   label = @Translation("Language field item"),
  *   description = @Translation("An entity field referencing a language."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList",
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList",
  *   constraints = {
  *     "ComplexData" = {
  *       "value" = {"Length" = {"max" = 12}}
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/StringItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/StringItem.php
index a243567..5efb359 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/StringItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/StringItem.php
@@ -10,6 +10,7 @@
 use Drupal\Core\TypedData\Annotation\DataType;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Entity\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'string_field' entity field item.
@@ -18,7 +19,7 @@
  *   id = "string_field",
  *   label = @Translation("String field item"),
  *   description = @Translation("An entity field containing a string value."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_type = "field_item_list"
  * )
  */
 class StringItem extends FieldItemBase {
@@ -38,10 +39,9 @@ class StringItem extends FieldItemBase {
   public function getPropertyDefinitions() {
 
     if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'string',
-        'label' => t('Text value'),
-      );
+      static::$propertyDefinitions['value'] = DataDefinition::create()
+        ->setDataType('string')
+        ->setLabel(t('Text value'));
     }
     return static::$propertyDefinitions;
   }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/UriItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/UriItem.php
index 6ce5970..3e3ae0a 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/UriItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/UriItem.php
@@ -18,7 +18,7 @@
  *   id = "uri_field",
  *   label = @Translation("URI field item"),
  *   description = @Translation("An entity field containing a URI."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList"
  * )
  */
 class UriItem extends FieldItemBase {
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/UuidItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/UuidItem.php
index 3e28741..3b0b2a6 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/UuidItem.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/UuidItem.php
@@ -19,7 +19,7 @@
  *   id = "uuid_field",
  *   label = @Translation("UUID field item"),
  *   description = @Translation("An entity field containing a UUID."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList",
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList",
  *   constraints = {
  *     "ComplexData" = {
  *       "value" = {"Length" = {"max" = 128}}
diff --git a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
index ec9ef6d..2bcd62d 100644
--- a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
+++ b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
@@ -25,8 +25,8 @@
  *
  * Furthermore, lists of data items are represented by objects implementing the
  * \Drupal\Core\TypedData\ListInterface. A list contains items of the same data
- * type, is ordered and may contain duplicates. The classed used for a list of
- * items of a certain type may be specified using the 'list class' key.
+ * type, is ordered and may contain duplicates. The data type used for a list of
+ * items of a certain type may be specified using the 'list_type' property.
  *
  * @see \Drupal::typedData()
  * @see \Drupal\Core\TypedData\TypedDataManager::create()
@@ -62,14 +62,6 @@ class DataType extends Plugin {
   public $description;
 
   /**
-   * The typed data class used for wrapping multiple data items of the type.
-   * Must implement the \Drupal\Core\TypedData\ListInterface.
-   *
-   * @var string
-   */
-  public $list_class = '\Drupal\Core\TypedData\ItemList';
-
-  /**
    * The pre-defined primitive type that this data type maps to.
    *
    * If set, it must be a constant defined by \Drupal\Core\TypedData\Primitive
@@ -88,4 +80,20 @@ class DataType extends Plugin {
    */
   public $constraints;
 
+  /**
+   * The list type used by default.
+   *
+   * A list of items is represented using a list data type, whose class
+   * implements the \Drupal\Core\TypedData\ListInterface.
+   * The specified list data type serves as default for lists of items based on
+   * the declared data type, while data definitions may specify the data type,
+   * or a custom class to use instead.
+   *
+   * @see \Drupal\Core\TypedData\ListDefinitionInterface
+   *
+   * @var string
+   *   The plugin ID of the list type.
+   */
+  public $list_type = 'list';
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 8acfaa3..2e3d0e7 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -105,10 +105,6 @@ public function getPropertyDefinition($name);
   /**
    * Gets an array of property definitions of contained properties.
    *
-   * @param array $definition
-   *   The definition of the container's property, e.g. the definition of an
-   *   entity reference property.
-   *
    * @return array
    *   An array of property definitions of contained properties, keyed by
    *   property name.
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php
new file mode 100644
index 0000000..fb3b0a5
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -0,0 +1,361 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataDefinition.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * A class for defining data based on defined data types.
+ */
+class DataDefinition implements DataDefinitionInterface, \ArrayAccess {
+
+  /**
+   * The array holding values for all definition keys.
+   *
+   * @var array
+   */
+  protected $definition = array();
+
+  /**
+   * Creates a new data definition.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   */
+  public static function create() {
+    $class = get_called_class();
+    return new $class();
+  }
+
+  /**
+   * Constructs a new data definition object.
+   *
+   * @param array $definition
+   *   (optional) If given, a data definition represented as array.
+   */
+  public function __construct(array $definition = array()) {
+    $this->definition = $definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataType() {
+    return !empty($this->definition['type']) ? $this->definition['type'] : 'any';
+  }
+
+  /**
+   * Sets the data type.
+   *
+   * @param string $type
+   *   The data type to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setDataType($type) {
+    $this->definition['type'] = $type;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return isset($this->definition['label']) ? $this->definition['label'] : NULL;
+  }
+
+  /**
+   * Sets the human-readable label.
+   *
+   * @param string $label
+   *   The label to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setLabel($label) {
+    $this->definition['label'] = $label;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return isset($this->definition['description']) ? $this->definition['description'] : NULL;
+  }
+
+  /**
+   * Sets the human-readable description.
+   *
+   * @param string $description
+   *   The description to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setDescription($description) {
+    $this->definition['description'] = $description;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isList() {
+    return ($this instanceof ListDefinitionInterface);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isReadOnly() {
+    if (!isset($this->definition['read-only'])) {
+      // Default to read-only if the data value is computed.
+      return $this->isComputed();
+    }
+    return !empty($this->definition['read-only']);
+  }
+
+  /**
+   * Sets whether the data is read-only.
+   *
+   * @param boolean $read-only
+   *   Whether the data is read-only.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setReadOnly($read_only) {
+    $this->definition['read-only'] = $read_only;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isComputed() {
+    return !empty($this->definition['computed']);
+  }
+
+  /**
+   * Sets whether the data is computed.
+   *
+   * @param boolean $computed
+   *   Whether the data is computed.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setComputed($computed) {
+    $this->definition['computed'] = $computed;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRequired() {
+    return !empty($this->definition['required']);
+  }
+
+  /**
+   * Sets whether the data is required.
+   *
+   * @param boolean $required
+   *   Whether the data is required.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setRequired($required) {
+    $this->definition['required'] = $required;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return isset($this->definition['class']) ? $this->definition['class'] : NULL;
+  }
+
+  /**
+   * Sets the class used for creating the typed data object.
+   *
+   * @param string|null $class
+   *   The class to use.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setClass($class) {
+    $this->definition['class'] = $class;
+    return $this;
+  }
+
+  /**
+   * Returns the array of settings, as required by the used class.
+   *
+   * See the documentation of the class for supported or required settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings() {
+    return isset($this->definition['settings']) ? $this->definition['settings'] : array();
+  }
+
+  /**
+   * Sets the array of settings, as required by the used class.
+   *
+   * @param array $settings
+   *   The array of settings.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setSettings(array $settings) {
+    $this->definition['settings'] = $settings;
+    return $this;
+  }
+
+  /**
+   * Returns an array of validation constraints.
+   *
+   * See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
+   *
+   * @return array
+   *   Array of constraints, each being an instance of
+   *   \Symfony\Component\Validator\Constraint.
+   */
+  public function getConstraints() {
+    return isset($this->definition['constraints']) ? $this->definition['constraints'] : array();
+  }
+
+  /**
+   * Sets the array of validation constraints.
+   *
+   * See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
+   *
+   * @param array $constraints
+   *   The array of constraints.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setConstraints(array $constraints) {
+    $this->definition['constraints'] = $constraints;
+    return $this;
+  }
+
+  /**
+   * Adds a validation constraint.
+   *
+   * See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
+   *
+   * @param string $constraint_name
+   *   The name of the constraint to add, i.e. its plugin id.
+   * @param array|null $options
+   *   The constraint options as required by the constraint plugin, or NULL.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function addConstraint($constraint_name, $options = NULL) {
+    $this->definition['constraints'][$constraint_name] = $options;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetExists($offset) {
+    // PHP's array access does not work correctly with isset(), so we have to
+    // bake isset() in here. See https://bugs.php.net/bug.php?id=41727.
+    return array_key_exists($offset, $this->definition) && isset($this->definition[$offset]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function &offsetGet($offset) {
+    if (!isset($this->definition[$offset])) {
+      $this->definition[$offset] = NULL;
+    }
+    return $this->definition[$offset];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetSet($offset, $value) {
+    $this->definition[$offset] = $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetUnset($offset) {
+    unset($this->definition[$offset]);
+  }
+
+  /**
+   * Returns all definition values as array.
+   *
+   * @return array
+   */
+  public function toArray() {
+    return $this->definition;
+  }
+
+  /**
+   * Allows creating data definition objects from old style definition arrays.
+   *
+   * @todo: Remove once no old-style definition arrays need to be supported.
+   */
+  public static function createFromOldStyleDefinition(array $definition) {
+    if (empty($definition['list'])) {
+      return new DataDefinition($definition);
+    }
+
+    // If the definition describes a list, separate the list item definition
+    // from the list definition.
+    unset($definition['list']);
+
+    $list_definition = $definition;
+    unset($list_definition['type']);
+
+    // Constraints, class and settings apply to the list item.
+    unset($list_definition['constraints']);
+    unset($list_definition['class']);
+    unset($list_definition['settings']);
+
+    $list_definition = new ListDefinition($list_definition);
+    if (isset($definition['list_class'])) {
+      $list_definition->setClass($definition['list_class']);
+    }
+    else {
+      $type_definition = \Drupal::typedData()->getDefinition($definition['type']);
+      if (isset($type_definition['list_class'])) {
+        $list_definition->setClass($type_definition['list_class']);
+      }
+    }
+
+    // Take care of the item definition now. 'required' applies to the list
+    // definition.
+    unset($definition['required']);
+    $item_definition = new DataDefinition($definition);
+    if (isset($definition['settings'])) {
+      $item_definition->setSettings($definition['settings']);
+    }
+    if (isset($definition['constraints'])) {
+      $item_definition->setConstraints($definition['constraints']);
+    }
+    $list_definition->setItemDefinition($item_definition);
+    return $list_definition;
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
new file mode 100644
index 0000000..d891a56
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataDefinitionInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for data definitions.
+ *
+ * Data definitions are used to describe data based upon available data types.
+ * For example, a plugin could describe its parameters using data definitions
+ * in order to specify what kind of data is required for it.
+ *
+ * @see \Drupal\Core\TypedData\DataDefinition
+ */
+interface DataDefinitionInterface {
+
+  /**
+   * Returns the data type of the data.
+   *
+   * @return string
+   *   The data type.
+   */
+  public function getDataType();
+
+  /**
+   * Returns a human readable label.
+   *
+   * @return string
+   *   The label.
+   */
+  public function getLabel();
+
+  /**
+   * Returns a human readable description.
+   *
+   * @return string|null
+   *   The description, or NULL if no description is available.
+   */
+  public function getDescription();
+
+  /**
+   * Returns whether the data is multi-valued, i.e. a list of data items.
+   *
+   * This is equivalent to checking whether the data definition implements the
+   * \Drupal\Core\TypedData\ListDefinitionInterface interface.
+   *
+   * @return boolean
+   *   Whether the data is multi-valued.
+   */
+  public function isList();
+
+  /**
+   * Determines whether the data is read-only.
+   *
+   * @return boolean
+   *   Whether the data is read-only.
+   */
+  public function isReadOnly();
+
+  /**
+   * Determines whether the data value is computed.
+   *
+   * For example, data could be computed depending on some other values.
+   *
+   * @return boolean
+   *   Whether the data value is computed.
+   */
+  public function isComputed();
+
+  /**
+   * Determines whether a data value is required.
+   *
+   * For required data a non-NULL value is mandatory.
+   *
+   * @return boolean
+   *   Whether a data value is required.
+   */
+  public function isRequired();
+
+  /**
+   * Returns the class used for creating the typed data object.
+   *
+   * If not specified, the default class of the data type will be used.
+   *
+   * @return string|null
+   *   The class used for creating the typed data object.
+   */
+  public function getClass();
+
+  /**
+   * Returns the array of settings, as required by the used class.
+   *
+   * See the documentation of the class for supported or required settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings();
+
+  /**
+   * Returns an array of validation constraints.
+   *
+   * See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
+   *
+   * @return array
+   *   Array of constraints, each being an instance of
+   *   \Symfony\Component\Validator\Constraint.
+   */
+  public function getConstraints();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ListDefinition.php b/core/lib/Drupal/Core/TypedData/ListDefinition.php
new file mode 100644
index 0000000..d00bce4
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ListDefinition.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\ListDefinition.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * A class for defining data based on defined data types.
+ */
+class ListDefinition extends DataDefinition implements ListDefinitionInterface {
+
+  /**
+   * The data definition of a list item.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface
+   */
+  protected $itemDefinition;
+
+  /**
+   * Creates a new list definition.
+   *
+   * @return \Drupal\Core\TypedData\ListDefinition
+   */
+  public static function create() {
+    $class = get_called_class();
+    return new $class();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $definition = array()) {
+    parent::__construct($definition);
+    $this->itemDefinition = new DataDefinition();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * The default data type of a list definition is derived from the list item's
+   * data type.
+   */
+  public function getDataType() {
+    if (!empty($this->definition['type'])) {
+      return $this->definition['type'];
+    }
+    // If a list definition is used but no list type has been specified, derive
+    // the default list type from the item type.
+    else {
+      $item_type_definition = \Drupal::typedData()
+        ->getDefinition($this->getItemDefinition()->getDataType());
+      // For BC check for a defined list class.
+      // @todo: Remove once all list_class usages have been converted.
+      if (isset($item_type_definition['list_class']) && !$this->getClass()) {
+        $this->setClass($item_type_definition['list_class']);
+      }
+      return isset($item_type_definition['list_type']) ? $item_type_definition['list_type'] : 'list';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getItemDefinition() {
+    return $this->itemDefinition;
+  }
+
+  /**
+   * Sets the item definition.
+   */
+  public function setItemDefinition(DataDefinitionInterface $definition) {
+    $this->itemDefinition = $definition;
+    return $this;
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php
new file mode 100644
index 0000000..c07d60e
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ListDefinitionInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\ListDefinitionInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for data definitions of lists.
+ *
+ * This interface is present on a data definition if it describes a list. The
+ * actual lists implement the \Drupal\Core\TypedData\ListInterface.
+ */
+interface ListDefinitionInterface extends DataDefinitionInterface {
+
+  /**
+   * Gets the data definition of an item of the list.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   *   A data definition describing the list items.
+   */
+  public function getItemDefinition();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 2088c2d..d5593ef 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -29,7 +29,7 @@ public function isEmpty();
   /**
    * Gets the definition of a contained item.
    *
-   * @return array
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
    *   The data definition of contained items.
    */
   public function getItemDefinition();
diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
similarity index 91%
rename from core/lib/Drupal/Core/TypedData/ItemList.php
rename to core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
index 9b39e83..e7dca89 100644
--- a/core/lib/Drupal/Core/TypedData/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
@@ -2,16 +2,28 @@
 
 /**
  * @file
- * Contains \Drupal\Core\TypedData\List.
+ * Contains \Drupal\Core\TypedData\Plugin\DataType\ItemList.
  */
 
-namespace Drupal\Core\TypedData;
+namespace Drupal\Core\TypedData\Plugin\DataType;
+
+use Drupal\Core\TypedData\Annotation\DataType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
  * A generic list class.
  *
  * This class can serve as list for any type of items.
  * Note: The class cannot be called "List" as list is a reserved PHP keyword.
+ *
+ * @DataType(
+ *   id = "list",
+ *   label = @Translation("List of items")
+ * )
  */
 class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
 
@@ -89,14 +101,6 @@ public function getString() {
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
-   */
-  public function getConstraints() {
-    // Apply the constraints to the list items only.
-    return array();
-  }
-
-  /**
    * Implements \ArrayAccess::offsetExists().
    */
   public function offsetExists($offset) {
@@ -140,7 +144,7 @@ protected function createItem($offset = 0, $value = NULL) {
    * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
    */
   public function getItemDefinition() {
-    return array('list' => FALSE) + $this->definition;
+    return $this->definition->getItemDefinition();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 031828a..6e021e1 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -20,7 +20,7 @@
   /**
    * The data definition.
    *
-   * @var array
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface
    */
   protected $definition;
 
@@ -41,7 +41,7 @@
   /**
    * Constructs a TypedData object given its definition and context.
    *
-   * @param array $definition
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
    *   The data definition.
    * @param string $name
    *   (optional) The name of the created property, or NULL if it is the root
@@ -52,7 +52,7 @@
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     $this->definition = $definition;
     $this->parent = $parent;
     $this->name = $name;
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
index bbdf4ea..bef5a36 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
@@ -18,7 +18,9 @@
    * Gets the data definition.
    *
    * @return array
-   *   The data definition array.
+   *   The data definition represented as array.
+   *
+   * @see \Drupal\Core\TypedData\DataDefinition
    */
   public function getDefinition();
 
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index bbc0d7f..0850e37 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -52,77 +52,48 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
   }
 
   /**
-   * Implements \Drupal\Component\Plugin\PluginManagerInterface::createInstance().
+   * Instantiates a typed data object.
    *
-   * @param string $plugin_id
-   *   The id of a plugin, i.e. the data type.
+   * @param string $data_type
+   *   The data type, for which a typed object should be instantiated.
    * @param array $configuration
-   *   The plugin configuration, i.e. the data definition.
-   * @param string $name
-   *   (optional) If a property or list item is to be created, the name of the
-   *   property or the delta of the list item.
-   * @param mixed $parent
-   *   (optional) If a property or list item is to be created, the parent typed
-   *   data object implementing either the ListInterface or the
-   *   ComplexDataInterface.
+   *   The plugin configuration array, i.e. an array with the following keys:
+   *   - data definition: The data definition object, i.e. an instance of
+   *     \Drupal\Core\TypedData\DataDefinitionInterface.
+   *   - name: (optional) If a property or list item is to be created, the name
+   *     of the property or the delta of the list item.
+   *   - parent: (optional) If a property or list item is to be created, the
+   *     parent typed data object implementing either the ListInterface or the
+   *     ComplexDataInterface.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The instantiated typed data object.
    */
-  public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
-    $type_definition = $this->getDefinition($plugin_id);
+  public function createInstance($data_type, array $configuration) {
+    $data_definition = $configuration['data_definition'];
+    $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' => $plugin_id)));
+      throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $data_type)));
     }
 
     // Allow per-data definition overrides of the used classes, i.e. take over
     // classes specified in the data definition.
-    $key = empty($configuration['list']) ? 'class' : 'list_class';
-    if (isset($configuration[$key])) {
-      $class = $configuration[$key];
-    }
-    elseif (isset($type_definition[$key])) {
-      $class = $type_definition[$key];
-    }
+    $class = $data_definition->getClass();
+    $class = isset($class) ? $class : $type_definition['class'];
 
     if (!isset($class)) {
-      throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
+      throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $data_type));
     }
-    return new $class($configuration, $name, $parent);
+    return new $class($data_definition, $configuration['name'], $configuration['parent']);
   }
 
   /**
    * Creates a new typed data object instance.
    *
-   * @param array $definition
-   *   The data definition array with the following array keys and values:
-   *   - type: The data type of the data to wrap. Required.
-   *   - label: A human readable label.
-   *   - description: A human readable description.
-   *   - list: Whether the data is multi-valued, i.e. a list of data items.
-   *     Defaults to FALSE.
-   *   - computed: A boolean specifying whether the data value is computed by
-   *     the object, e.g. depending on some other values.
-   *   - read-only: A boolean specifying whether the data is read-only. Defaults
-   *     to TRUE for computed properties, to FALSE otherwise.
-   *   - class: If set and 'list' is FALSE, the class to use for creating the
-   *     typed data object; otherwise the default class of the data type will be
-   *     used.
-   *   - list_class: If set and 'list' is TRUE, the class to use for creating
-   *     the typed data object; otherwise the default list class of the data
-   *     type will be used.
-   *   - settings: An array of settings, as required by the used 'class'. See
-   *     the documentation of the class for supported or required settings.
-   *   - list_settings: An array of settings as required by the used
-   *     'list_class'. See the documentation of the list class for support or
-   *     required settings.
-   *   - constraints: An array of validation constraints. See
-   *     \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
-   *   - required: A boolean specifying whether a non-NULL value is mandatory.
-   *   Further keys may be supported in certain usages, e.g. for further keys
-   *   supported for entity field definitions see
-   *   \Drupal\Core\Entity\StorageControllerInterface::getPropertyDefinitions().
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
+   *   The data definition of the typed data object. For backwards-compatibility
+   *   an array representation of the data definition may be passed also.
    * @param mixed $value
    *   (optional) The data value. If set, it has to match one of the supported
    *   data type format as documented for the data type classes.
@@ -147,14 +118,20 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
    * @see \Drupal\Core\TypedData\Plugin\DataType\Date
    * @see \Drupal\Core\TypedData\Plugin\DataType\Uri
    * @see \Drupal\Core\TypedData\Plugin\DataType\Binary
-   * @see \Drupal\Core\Entity\Field\EntityWrapper
    */
-  public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
-    $wrapper = $this->createInstance($definition['type'], $definition, $name, $parent);
+  public function create($definition, $value = NULL, $name = NULL, $parent = NULL) {
+    if (is_array($definition)) {
+      $definition = DataDefinition::createFromOldStyleDefinition($definition);
+    }
+    $typed_data = $this->createInstance($definition->getDataType(), array(
+      'data_definition' => $definition,
+      'name' => $name,
+      'parent' => $parent,
+    ));
     if (isset($value)) {
-      $wrapper->setValue($value, FALSE);
+      $typed_data->setValue($value, FALSE);
     }
-    return $wrapper;
+    return $typed_data;
   }
 
   /**
@@ -336,18 +313,20 @@ public function getValidationConstraintManager() {
    *
    * @see \Drupal\Core\Validation\ConstraintManager
    *
-   * @param array $definition
-   *   A data definition array.
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
+   *   A data definition.
    *
    * @return array
    *   Array of constraints, each being an instance of
    *   \Symfony\Component\Validator\Constraint.
+   *
+   * @todo: Having this as well as $definition->getConstraints() is confusing.
    */
-  public function getConstraints($definition) {
+  public function getConstraints(DataDefinitionInterface $definition) {
     $constraints = array();
     $validation_manager = $this->getValidationConstraintManager();
 
-    $type_definition = $this->getDefinition($definition['type']);
+    $type_definition = $this->getDefinition($definition->getDataType());
     // Auto-generate a constraint for data types implementing a primitive
     // interface.
     if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
@@ -364,19 +343,21 @@ public function getConstraints($definition) {
       }
     }
     // Add any constraints specified as part of the data definition.
-    if (isset($definition['constraints'])) {
-      foreach ($definition['constraints'] as $name => $options) {
-        $constraints[] = $validation_manager->create($name, $options);
-      }
+    $defined_constraints = $definition->getConstraints();
+    foreach ($defined_constraints as $name => $options) {
+      $constraints[] = $validation_manager->create($name, $options);
     }
     // Add the NotNull constraint for required data.
-    if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
+    if ($definition->isRequired() && !isset($defined_constraints['NotNull'])) {
       $constraints[] = $validation_manager->create('NotNull', array());
     }
 
     // If the definition does not provide a class use the class from the type
     // definition for performing interface checks.
-    $class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
+    $class = $definition->getClass();
+    if (!$class) {
+      $class = $type_definition['class'];
+    }
     // Check if the class provides allowed values.
     if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
       $constraints[] = $validation_manager->create('AllowedValues', array());
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php
index ba827e6..647188d 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php
@@ -19,6 +19,9 @@ class ValidReferenceConstraintValidator extends ConstraintValidator {
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
+    if (!isset($value)) {
+      return;
+    }
     $id = $value->get('target_id')->getValue();
     // '0' or NULL are considered valid empty references.
     if (empty($id)) {
diff --git a/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php b/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php
index c0f6123..d6954b9 100644
--- a/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php
+++ b/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php
@@ -29,7 +29,7 @@ class DateTimeComputed extends TypedData {
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
     if (!isset($definition['settings']['date source'])) {
       throw new \InvalidArgumentException("The definition's 'date source' key has to specify the name of the date property to be computed.");
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 339e255..2ae7c02 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -186,7 +186,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']);
-  if ($definition && empty($definition['computed'])) {
+  if ($definition && !$definition->isComputed()) {
     $variables['attributes']['data-edit-id'] = $entity->entityType() . '/' . $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 6364102..7807fc6 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -550,7 +550,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) {
   // Find all configurable fields, because only they could have a
   // text_processing setting.
   $configurable_fields = array_keys(array_filter($properties, function ($definition) {
-    return isset($definition['configurable']) && $definition['configurable'] === TRUE;
+    return $definition->isFieldConfigurable();
   }));
   if (empty($configurable_fields)) {
     return array();
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index 7b379e4..1231b43 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -23,34 +23,6 @@ function entity_reference_field_info_alter(&$info) {
 }
 
 /**
- * Implements hook_entity_field_info_alter().
- *
- * Set the "target_type" property definition for entity reference fields.
- *
- * @see \Drupal\Core\Entity\Plugin\DataType\EntityReferenceItem::getPropertyDefinitions()
- *
- * @param array $info
- *   The property info array as returned by hook_entity_field_info().
- * @param string $entity_type
- *   The entity type for which entity properties are defined.
- */
-function entity_reference_entity_field_info_alter(&$info, $entity_type) {
-  foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
-    foreach ($instances as $field_name => $instance) {
-      if ($instance->getFieldType() != 'entity_reference') {
-        continue;
-      }
-      if (isset($info['definitions'][$field_name])) {
-        $info['definitions'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
-      }
-      elseif (isset($info['optional'][$field_name])) {
-        $info['optional'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
-      }
-    }
-  }
-}
-
-/**
  * Implements hook_field_widget_info_alter().
  */
 function entity_reference_field_widget_info_alter(&$info) {
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 84fd041..41c3f5f 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -195,16 +195,16 @@ function field_entity_field_info($entity_type) {
 
   foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
     $optional = $bundle_name != $entity_type;
+    // @todo: Improve hook_entity_field_info() so that it can take field
+    // instance also.
 
     foreach ($instances as $field_name => $instance) {
-      $definition = _field_generate_entity_field_definition($instance->getField());
-
       if ($optional) {
-        $property_info['optional'][$field_name] = $definition;
+        $property_info['optional'][$field_name] = $instance->getField();
         $property_info['bundle map'][$bundle_name][] = $field_name;
       }
       else {
-        $property_info['definitions'][$field_name] = $definition;
+        $property_info['definitions'][$field_name] = $instance->getField();
       }
     }
   }
@@ -213,33 +213,6 @@ function field_entity_field_info($entity_type) {
 }
 
 /**
- * Generates an entity field definition for a configurable field.
- *
- * @param \Drupal\field\FieldInterface $field
- *   The field definition.
- * @param \Drupal\field\FieldInstanceInterface $instance
- *   (Optional) The field instance definition.
- *
- * @return array
- *   The entity field definition.
- */
-function _field_generate_entity_field_definition(FieldInterface $field, FieldInstanceInterface $instance = NULL) {
-  // @todo: Allow for adding field type settings.
-  $definition = array(
-    'label' => t('Field !name', array('!name' => $field->name)),
-    'type' => 'field_item:' . $field->type,
-    'list' => TRUE,
-    'configurable' => TRUE,
-    'translatable' => !empty($field->translatable),
-  );
-  if ($instance) {
-    $definition['instance'] = $instance;
-  }
-
-  return $definition;
-}
-
-/**
  * Implements hook_field_widget_info_alter().
  */
 function field_field_widget_info_alter(&$info) {
diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php
index 463f5cb..11cef9b 100644
--- a/core/modules/field/lib/Drupal/field/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\field\FieldException;
 use Drupal\field\FieldInterface;
 
@@ -199,6 +200,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $original = NULL;
 
   /**
+   * The data definition of a field item.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinition
+   */
+  protected $itemDefinition;
+
+  /**
    * Constructs a Field object.
    *
    * @param array $values
@@ -582,7 +590,7 @@ public function getFieldLabel() {
    * {@inheritdoc}
    */
   public function getFieldDescription() {
-    return '';
+    return NULL;
   }
 
   /**
@@ -649,6 +657,20 @@ public function hasData() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldConfigurable() {
+    return TRUE;
+  }
+
+  /**
    * Implements the magic __sleep() method.
    *
    * Using the Serialize interface and serialize() / unserialize() methods
@@ -672,8 +694,97 @@ public function __wakeup() {
   /**
    * {@inheritdoc}
    */
-  public function isFieldConfigurable() {
+  public function getDataType() {
+    // Derive list type from the item type.
+    $item_type_definition = \Drupal::typedData()
+      ->getDefinition($this->getItemDefinition()->getDataType());
+    return isset($item_type_definition['list_type']) ? $item_type_definition['list_type'] : 'field_item_list';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return $this->label();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->getFieldDescription();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isList() {
     return TRUE;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isReadOnly() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isComputed() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRequired() {
+    return $this->isFieldRequired();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    // For BC check for a defined list class.
+    // @todo: Remove once all list_class usages have been converted.
+    $item_type_definition = \Drupal::typedData()
+      ->getDefinition($this->getItemDefinition()->getDataType());
+
+    if (isset($item_type_definition['list_class'])) {
+      return $item_type_definition['list_class'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSettings() {
+    // This should actually return the settings for field item list, which are
+    // not the field settings. However, there is no harm in returning field
+    // settings here, so we do that to avoid confusion for now.
+    // @todo: Unify with getFieldSettings() or remove once typed data moved
+    // to the adapter approach.
+    return $this->getFieldSettings();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getItemDefinition() {
+    if (!isset($this->itemDefinition)) {
+      $this->itemDefinition = DataDefinition::create()
+        ->setDataType('field_item:' . $this->type)
+        ->setSettings($this->getFieldSettings());
+    }
+    return $this->itemDefinition;
+  }
 }
diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
index d038977..93821ef 100644
--- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
@@ -210,6 +210,13 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
   public $original = NULL;
 
   /**
+   * The data definition of a field item.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinition
+   */
+  protected $itemDefinition;
+
+  /**
    * Constructs a FieldInstance object.
    *
    * @param array $values
@@ -614,7 +621,14 @@ public function isFieldConfigurable() {
     return TRUE;
   }
 
-  /*
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return TRUE;
+  }
+
+  /**
    * Implements the magic __sleep() method.
    *
    * Using the Serialize interface and serialize() / unserialize() methods
@@ -635,4 +649,97 @@ public function __wakeup() {
     $this->__construct($values);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataType() {
+    // Derive list type from the item type.
+    $item_type_definition = \Drupal::typedData()
+      ->getDefinition($this->getItemDefinition()->getDataType());
+    return isset($item_type_definition['list_type']) ? $item_type_definition['list_type'] : 'field_item_list';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return $this->label();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->getFieldDescription();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isList() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isReadOnly() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isComputed() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRequired() {
+    // Only field instances can be required.
+    return $this->isFieldRequired();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    // For BC check for a defined list class.
+    // @todo: Remove once all list_class usages have been converted.
+    $item_type_definition = \Drupal::typedData()
+      ->getDefinition($this->getItemDefinition()->getDataType());
+
+    if (isset($item_type_definition['list_class'])) {
+      return $item_type_definition['list_class'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSettings() {
+    return $this->getFieldSettings();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getItemDefinition() {
+    if (!isset($this->itemDefinition)) {
+      $this->itemDefinition = DataDefinition::create()
+        ->setDataType('field_item:' . $this->field->type)
+        ->setSettings($this->getFieldSettings());
+    }
+    return $this->itemDefinition;
+  }
+
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php
index 32bea80..3000e56 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigFieldItemList.php
@@ -8,8 +8,9 @@
 namespace Drupal\field\Plugin\Type\FieldType;
 
 use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\Core\Entity\Field\FieldItemList;
+use Drupal\Core\Entity\Plugin\DataType\FieldItemList;
 use Drupal\field\Field as FieldAPI;
+use Drupal\field\FieldInstanceInterface;
 
 /**
  * Represents a configurable entity field item list.
@@ -26,10 +27,11 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
-    if (isset($definition['instance'])) {
-      $this->instance = $definition['instance'];
+    // Definition can be the field config or field instance.
+    if ($definition instanceof FieldInstanceInterface) {
+      $this->instance = $definition;
     }
   }
 
@@ -69,13 +71,6 @@ public function getConstraints() {
   /**
    * {@inheritdoc}
    */
-  protected function getDefaultValue() {
-    return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function defaultValuesForm(array &$form, array &$form_state) {
     if (empty($this->getFieldDefinition()->default_value_function)) {
       // Place the input in a separate place in the submitted values tree.
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItemList.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItemList.php
index 22cd8a0..5d20b0c 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItemList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItemList.php
@@ -102,8 +102,8 @@ public function deleteRevision() {
    *   The name of the hook, e.g. 'presave', 'validate'.
    */
   protected function legacyCallback($hook, $args = array()) {
-    $definition = $this->getPluginDefinition();
-    $module = $definition['provider'];
+    $type_definition = \Drupal::typedData()->getDefinition($this->getItemDefinition()->getDataType());
+    $module = $type_definition['provider'];
     $callback = "{$module}_field_{$hook}";
     if (function_exists($callback)) {
       // We need to remove the empty "prototype" item here.
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
index 5d6b200..4abf5d3 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
@@ -11,6 +11,7 @@
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\Config\Schema\Element;
 use Drupal\Core\Config\Schema\ArrayElement;
+use Drupal\Core\TypedData\DataDefinitionInterface;
 
 /**
  * Defines the locale configuration wrapper object.
@@ -41,7 +42,7 @@ class LocaleTypedConfig extends Element {
   /**
    * Constructs a configuration wrapper object.
    *
-   * @param array $definition
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
    *   The data definition.
    * @param string $name
    *   The configuration object name.
@@ -50,7 +51,7 @@ class LocaleTypedConfig extends Element {
    * @param \Drupal\locale\LocaleConfigManager $localeConfig;
    *   The locale configuration manager object.
    */
-  public function __construct(array $definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
+  public function __construct($definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
     parent::__construct($definition, $name);
     $this->langcode = $langcode;
     $this->localeConfig = $localeConfig;
diff --git a/core/modules/path/lib/Drupal/path/Plugin/DataType/PathItem.php b/core/modules/path/lib/Drupal/path/Plugin/DataType/PathItem.php
index 11059b5..7a0f9fe 100644
--- a/core/modules/path/lib/Drupal/path/Plugin/DataType/PathItem.php
+++ b/core/modules/path/lib/Drupal/path/Plugin/DataType/PathItem.php
@@ -18,7 +18,7 @@
  *   id = "path_field",
  *   label = @Translation("Path field item"),
  *   description = @Translation("An entity field containing a path alias and related data."),
- *   list_class = "\Drupal\Core\Entity\Field\FieldItemList"
+ *   list_class = "\Drupal\Core\Entity\Plugin\DataType\FieldItemList"
  * )
  */
 class PathItem extends FieldItemBase {
diff --git a/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php b/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php
index 399d8c1..8704c7f 100644
--- a/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php
+++ b/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php
@@ -9,8 +9,7 @@
 
 use Drupal\Tests\UnitTestCase;
 use Drupal\serialization\Normalizer\ListNormalizer;
-use Drupal\Core\TypedData\ItemList;
-use Drupal\Core\TypedData\Plugin\DataType\Integer;
+use Drupal\Core\TypedData\Plugin\DataType\ItemList;
 
 /**
  * Tests the ListNormalizer class.
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 f96dfb1..e2e6c5a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -353,21 +353,18 @@ public function testIntrospection() {
    */
   protected function checkIntrospection($entity_type) {
     // Test getting metadata upfront.
-    // @todo: Make this work without having to create entity objects.
-    $entity = entity_create($entity_type, array());
-    $definitions = $entity->getPropertyDefinitions();
-    $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $entity_type .': User field found.');
-    $this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
+    $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type);
+    $this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
+    $this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
+    $this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
 
     // Test introspecting an entity object.
     // @todo: Add bundles and test bundles as well.
     $entity = entity_create($entity_type, array());
-
     $definitions = $entity->getPropertyDefinitions();
-    $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $entity_type .': User field found.');
-    $this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
+    $this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
+    $this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
+    $this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
 
     $name_properties = $entity->name->getPropertyDefinitions();
     $this->assertEqual($name_properties['value']['type'], 'string', $entity_type .': String value property of the name found.');
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 f8b5306..0933752 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\TypedData;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\TypedData\ListDefinition;
 use Drupal\simpletest\DrupalUnitTestBase;
 use Drupal\Core\Datetime\DrupalDateTime;
 
@@ -313,10 +314,9 @@ public function testGetAndSet() {
   public function testTypedDataLists() {
     // Test working with an existing list of strings.
     $value = array('one', 'two', 'three');
-    $typed_data = $this->createTypedData(array(
-      'type' => 'string',
-      'list' => TRUE,
-    ), $value);
+    $list_definition = ListDefinition::create();
+    $list_definition->getItemDefinition()->setDataType('string');
+    $typed_data = $this->createTypedData($list_definition, $value);
     $this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
     // Test iterating.
     $count = 0;
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
index 066e8f2..ed707fa 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
@@ -11,7 +11,9 @@
 use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Entity\Field\FieldDefinition;
 use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\taxonomy\TermInterface;
 
 /**
@@ -203,67 +205,66 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $
    * {@inheritdoc}
    */
   public static function baseFieldDefinitions($entity_type) {
-    $properties['tid'] = array(
-      'label' => t('Term ID'),
-      'description' => t('The term ID.'),
-      'type' => 'integer_field',
-      'read-only' => TRUE,
-    );
-    $properties['uuid'] = array(
-      'label' => t('UUID'),
-      'description' => t('The term UUID.'),
-      'type' => 'uuid_field',
-      'read-only' => TRUE,
-    );
-    $properties['vid'] = array(
-      'label' => t('Vocabulary ID'),
-      'description' => t('The ID of the vocabulary to which the term is assigned.'),
-      'type' => 'string_field',
-    );
-    $properties['langcode'] = array(
-      'label' => t('Language code'),
-      'description' => t('The term language code.'),
-      'type' => 'language_field',
-    );
-    $properties['name'] = array(
-      'label' => t('Name'),
-      'description' => t('The term name.'),
-      'type' => 'string_field',
-    );
-    $properties['description'] = array(
-      'label' => t('Description'),
-      'description' => t('A description of the term'),
-      'type' => 'string_field',
-    );
+
+    $fields['tid'] = FieldDefinition::create()
+      ->setLabel(t('Term ID'))
+      ->setDescription(t('The term ID.'))
+      ->setFieldType('integer')
+      ->setReadOnly(TRUE);
+
+    $fields['uuid'] = FieldDefinition::create()
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The term UUID.'))
+      ->setFieldType('uuid')
+      ->setReadOnly(TRUE);
+
+    $fields['vid'] = FieldDefinition::create()
+      ->setLabel(t('Vocabulary ID'))
+      ->setDescription(t('The ID of the vocabulary to which the term is assigned.'))
+      ->setFieldType('string');
+
+    $fields['langcode'] = FieldDefinition::create()
+      ->setLabel(t('Language code'))
+      ->setDescription(t('The term language code.'))
+      ->setFieldType('language');
+
+    $fields['name'] = FieldDefinition::create()
+      ->setLabel(t('Name'))
+      ->setDescription(t('The term name.'))
+      ->setFieldType('string');
+
+    $fields['description'] = FieldDefinition::create()
+      ->setLabel(t('Description'))
+      ->setDescription(t('A description of the term.'))
+      ->setFieldType('string');
+
     // @todo Combine with description.
-    $properties['format'] = array(
-      'label' => t('Description format'),
-      'description' => t('The filter format ID of the description.'),
-      'type' => 'string_field',
-    );
-    $properties['weight'] = array(
-      'label' => t('Weight'),
-      'description' => t('The weight of this term in relation to other terms.'),
-      'type' => 'integer_field',
-      'settings' => array('default_value' => 0),
-    );
-    $properties['parent'] = array(
-      'label' => t('Term Parents'),
-      'description' => t('The parents of this term.'),
-      'type' => 'integer_field',
+    $fields['format'] = FieldDefinition::create()
+      ->setLabel(t('Description format'))
+      ->setDescription(t('The filter format ID of the description.'))
+      ->setFieldType('string');
+
+    $fields['weight'] = FieldDefinition::create()
+      ->setLabel(t('Weight'))
+      ->setDescription(t('The weight of this term in relation to other terms.'))
+      ->setFieldType('integer')
+      ->setFieldSetting('default_value', 0);
+
+    $fields['parent'] = FieldDefinition::create()
+      ->setLabel(t('Term Parents'))
+      ->setDescription(t('The parents of this term.'))
+      ->setFieldType('integer')
       // Save new terms with no parents by default.
-      'settings' => array('default_value' => 0),
-      'computed' => TRUE,
-    );
-    $properties['changed'] = array(
-      'label' => t('Changed'),
-      'description' => t('The time that the term was last edited.'),
-      'type' => 'integer_field',
-      'property_constraints' => array(
-        'value' => array('EntityChanged' => array()),
-      ),
-    );
-    return $properties;
+      ->setFieldSetting('default_value', 0)
+      ->setComputed(TRUE);
+
+    $fields['changed'] = FieldDefinition::create()
+      ->setLabel(t('Changed'))
+      ->setDescription(t('The time that the term was last edited.'))
+      ->setFieldType('integer')
+      ->setPropertyConstraints('value', array('EntityChanged' => array()));
+
+    return $fields;
   }
 
   /**
diff --git a/core/modules/text/lib/Drupal/text/TextProcessed.php b/core/modules/text/lib/Drupal/text/TextProcessed.php
index bbf0fd0..dd1fe8e 100644
--- a/core/modules/text/lib/Drupal/text/TextProcessed.php
+++ b/core/modules/text/lib/Drupal/text/TextProcessed.php
@@ -29,7 +29,7 @@ class TextProcessed extends TypedData {
   /**
    * Overrides TypedData::__construct().
    */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+  public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
 
     if (!isset($definition['settings']['text source'])) {
diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
index 23f5a64..73a2ed9 100644
--- a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
@@ -26,6 +26,34 @@ public static function getInfo() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Prepare a container with a mock typed data object, that returns no
+    // type definitions.
+    // @todo: Remove this once all field types are converted and getFieldType()
+    // does not need the manager any more.
+    $typed_data = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $typed_data
+      ->expects($this->any())
+      ->method('getDefinition')
+      ->will($this->returnValue(NULL));
+
+    $container = $this->getMock('Drupal\Core\DependencyInjection\Container');
+    $container
+      ->expects($this->any())
+      ->method('get')
+      ->will($this->returnValue($typed_data));
+
+    \Drupal::setContainer($container);
+  }
+
+  /**
    * Tests field name methods.
    */
   public function testFieldName() {
