diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 4766a91..557ab80 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;
@@ -354,15 +355,8 @@ public function getAdminPath($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()
@@ -393,12 +387,16 @@ 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($field_name, $definition);
+          }
         }
-        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($field_name, $definition);
+          }
         }
         $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/Field.php b/core/lib/Drupal/Core/Entity/Field/Field.php
index 0ff36d7..475b434 100644
--- a/core/lib/Drupal/Core/Entity/Field/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Field.php
@@ -10,7 +10,7 @@
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\Core\TypedData\ItemList;
+use Drupal\Core\TypedData\Plugin\DataType\ItemList;
 
 /**
  * Represents an entity field; that is, a list of field item objects.
@@ -52,8 +52,7 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
-    // @todo https://drupal.org/node/1988612
-    return NULL;
+    return new FieldDefinition($this->definition);
   }
 
   /**
@@ -82,7 +81,7 @@ public function getValue($include_computed = FALSE) {
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\ItemList::setValue().
+   * Overrides \Drupal\Core\TypedData\Plugin\DataType\ItemList::setValue().
    */
   public function setValue($values, $notify = TRUE) {
     if (!isset($values) || $values === array()) {
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
new file mode 100644
index 0000000..dc59bcf
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
@@ -0,0 +1,199 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Field\FieldDefinition.
+ */
+
+namespace Drupal\Core\Entity\Field;
+use Drupal\Core\TypedData\DataDefinition;
+
+/**
+ * A class for defining entity fields.
+ */
+class FieldDefinition extends DataDefinition implements FieldDefinitionInterface, \ArrayAccess {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldName() {
+    return $this->definition['field_name'];
+  }
+
+  /**
+   * Sets the field name.
+   *
+   * @param string $name
+   *   The field name to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setFieldName($name) {
+    $this->definition['field_name'] = $name;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldType() {
+    // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
+    $parts = explode(':', $this->getItemDefinition()->getType());
+    return $parts[1];
+  }
+
+  /**
+   * Sets the field type.
+   *
+   * @param string $type
+   *   The field type to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setFieldType($type) {
+    $this->definition['type'] = 'entity_field';
+    $this->definition['item_definition']['type'] = 'field_item:' . $type;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldSettings() {
+    return $this->definition['item_definition']['settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldSetting($setting_name) {
+    return $this->definition['item_definition']['settings'][$setting_name];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldPropertyNames() {
+    return array_keys(\Drupal::typedData()->create($this->getItemDefinition())->getPropertyDefinitions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldTranslatable() {
+    return !empty($this->definition['translatable']);
+  }
+
+  /**
+   * Sets whether the field is translatable.
+   *
+   * @param boolean $translatable
+   *   Whether the field is translatable.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setTranslatable($translatable) {
+    $this->definition['translatable'] = $translatable;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldLabel() {
+    return $this->definition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldDescription() {
+    return $this->definition['description'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldCardinality() {
+    // @todo: Support reading out a possible cardinality constraint?
+    return $this->isList() ? FIELD_CARDINALITY_UNLIMITED : 1;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldRequired() {
+    return !empty($this->definition['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\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setQueryable($queryable) {
+    $this->definition['queryable'] = $queryable;
+    return $this;
+  }
+
+  /**
+   * Sets constraints for a given field item property.
+   *
+   * @param string $name
+   *   The name of the property to set constraints for.
+   * @param array $constraints
+   *   The constraints to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setPropertyConstraints($name, array $constraints) {
+    $this->definition['item_definition']['constraints']['ComplexData'][$name] = $constraints;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldConfigurable() {
+    return FALSE;
+  }
+
+  /**
+   * 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($field_name, array $definition) {
+    unset($definition['list']);
+
+    $list_definition = $definition;
+    $list_definition['type'] = 'entity_field';
+    unset($list_definition['constraints']);
+    unset($list_definition['settings']);
+    $new = new FieldDefinition($list_definition);
+
+    if (isset($definition['list_class'])) {
+      $new->setClass($definition['list_class']);
+    }
+    // Apply the rest to the item definition.
+    $new->setItemDefinition(new FieldDefinition($definition));
+    $new->setFieldName($field_name);
+    return $new;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
index 386d47d..c2cb9f7 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php
@@ -48,6 +48,8 @@
  * abstractly, and present Views configuration options to the administrator
  * based on that abstract definition, even though that abstract definition can
  * differ from the concrete definition of any particular node's body field.
+ *
+ * @todo Extends this from DataDefinitionInterface.
  */
 interface FieldDefinitionInterface {
 
@@ -66,10 +68,9 @@ public function getFieldName();
    * Returns the field type.
    *
    * @return string
-   *   The field type.
+   *   The field type, i.e. the id of a field type plugin. For example 'text'.
    *
-   * @todo Provide more information about field types after
-   *   https://drupal.org/node/1969728 is implemented.
+   * @see \Drupal\Core\Entity\Field\FieldTypePluginManager
    */
   public function getFieldType();
 
@@ -121,6 +122,20 @@ public function getFieldPropertyNames();
   public function isFieldTranslatable();
 
   /**
+   * Determines whether the field is configurable via field.module.
+   *
+   * @return bool
+   */
+  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
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Field.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Field.php
new file mode 100644
index 0000000..5200370
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Field.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Plugin\DataType\Field.
+ */
+
+namespace Drupal\Core\Entity\Plugin\DataType;
+
+use Drupal\Core\TypedData\Annotation\DataType;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines a data type plugin for entity fields, i.e. the list of field items.
+ *
+ * Note that the class only register the plugin, and is actually never used.
+ * @todo: Move the implementation to this place also.
+ *
+ * @DataType(
+ *   id = "entity_field",
+ *   label = @Translation("Entity field"),
+ *   class = "\Drupal\Core\Entity\Field\Field"
+ * )
+ */
+class Field extends \Drupal\Core\Entity\Field\Field {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
index c0569ab..e28952d 100644
--- a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
+++ b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
@@ -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
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php
new file mode 100644
index 0000000..25c3cb2
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -0,0 +1,307 @@
+<?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();
+
+  /**
+   * 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 getType() {
+    return $this->definition['type'];
+  }
+
+  /**
+   * Sets the data type.
+   *
+   * @param string $type
+   *   The data type to set.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setType($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() {
+    // For BC, support definitions using the 'list' flag with 'list_class'.
+    $type_definition = \Drupal::typedData()->getDefinition($this->getType());
+    return is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\ListInterface') || !empty($this->definition['list']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isReadOnly() {
+    if (!isset($this->definition['read-only'])) {
+      // Default to read-only if the data value is computed.
+      return $this->isComputed();
+    }
+    return $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.
+   *
+   * @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;
+  }
+
+  /**
+   * Gets
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
+   *   If the data is a list, a data definition describing the list items, NULL
+   *   otherwise.
+   */
+  public function getItemDefinition() {
+    return isset($this->definition['item_definition']) ? $this->definition['item_definition'] : NULL;
+  }
+
+  /**
+   * Sets the data definition of an item of the list.
+   *
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface|null $definition
+   *   The data definition of an item of the list, or NULL if the data is no
+   *   list.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   The object itself for chaining.
+   */
+  public function setItemDefinition(DataDefinitionInterface $item_definition = NULL) {
+    $this->definition['item_definition'] = $item_definition;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetExists($offset) {
+    return array_key_exists($offset, $this->definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetGet($offset) {
+    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;
+  }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
new file mode 100644
index 0000000..4c53db1
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataDefinitionInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for data definitions.
+ *
+ * Data definitions are used to define typed data objects based upon available
+ * data types. For example, a plugin could describe some required parameters
+ * using data definitions.
+ *
+ * @see \Drupal\Core\TypedData\DataDefinition
+ */
+interface DataDefinitionInterface {
+
+  /**
+   * Returns the data type of the data.
+   *
+   * @return string
+   *   The data type.
+   */
+  public function getType();
+
+  /**
+   * Returns a human readable label.
+   *
+   * @return string
+   *   The label.
+   */
+  public function getLabel();
+
+  /**
+   * Returns a human readable description.
+   *
+   * @return string
+   *   The description.
+   */
+  public function getDescription();
+
+  /**
+   * Returns whether the data is multi-valued, i.e. a list of data items.
+   *
+   * Lists implement the \Drupal\Core\TypedData\ListInterface.
+   *
+   * @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();
+
+  /**
+   * Gets the data definition of an item of the list.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
+   *   If the data is a list, a data definition describing the list items, NULL
+   *   otherwise.
+   */
+  public function getItemDefinition();
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/ItemList.php
deleted file mode 100644
index 9b39e83..0000000
--- a/core/lib/Drupal/Core/TypedData/ItemList.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\TypedData\List.
- */
-
-namespace Drupal\Core\TypedData;
-
-/**
- * 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.
- */
-class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
-
-  /**
-   * Numerically indexed array items.
-   *
-   * @var array
-   */
-  protected $list = array();
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
-   */
-  public function getValue() {
-    if (isset($this->list)) {
-      $values = array();
-      foreach ($this->list as $delta => $item) {
-        $values[$delta] = $item->getValue();
-      }
-      return $values;
-    }
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
-   *
-   * @param array|null $values
-   *   An array of values of the field items, or NULL to unset the field.
-   */
-  public function setValue($values, $notify = TRUE) {
-    if (!isset($values) || $values === array()) {
-      $this->list = $values;
-    }
-    else {
-      if (!is_array($values)) {
-        throw new \InvalidArgumentException('Cannot set a list with a non-array value.');
-      }
-
-      // Clear the values of properties for which no value has been passed.
-      if (isset($this->list)) {
-        $this->list = array_intersect_key($this->list, $values);
-      }
-
-      // Set the values.
-      foreach ($values as $delta => $value) {
-        if (!is_numeric($delta)) {
-          throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
-        }
-        elseif (!isset($this->list[$delta])) {
-          $this->list[$delta] = $this->createItem($delta, $value);
-        }
-        else {
-          $this->list[$delta]->setValue($value);
-        }
-      }
-    }
-    // Notify the parent of any changes.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getString().
-   */
-  public function getString() {
-    $strings = array();
-    if (isset($this->list)) {
-      foreach ($this->list as $item) {
-        $strings[] = $item->getString();
-      }
-      // Remove any empty strings resulting from empty items.
-      return implode(', ', array_filter($strings, 'drupal_strlen'));
-    }
-  }
-
-  /**
-   * 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) {
-    return isset($this->list) && array_key_exists($offset, $this->list) && $this->offsetGet($offset)->getValue() !== NULL;
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetUnset().
-   */
-  public function offsetUnset($offset) {
-    if (isset($this->list)) {
-      unset($this->list[$offset]);
-    }
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetGet().
-   */
-  public function offsetGet($offset) {
-    if (!is_numeric($offset)) {
-      throw new \InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
-    }
-    // Allow getting not yet existing items as well.
-    // @todo: Maybe add a public createItem() method in addition?
-    elseif (!isset($this->list[$offset])) {
-      $this->list[$offset] = $this->createItem($offset);
-    }
-    return $this->list[$offset];
-  }
-
-  /**
-   * Helper for creating a list item object.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   */
-  protected function createItem($offset = 0, $value = NULL) {
-    return \Drupal::typedData()->getPropertyInstance($this, $offset, $value);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
-   */
-  public function getItemDefinition() {
-    return array('list' => FALSE) + $this->definition;
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetSet().
-   */
-  public function offsetSet($offset, $value) {
-    if (!isset($offset)) {
-      // The [] operator has been used so point at a new entry.
-      $offset = $this->list ? max(array_keys($this->list)) + 1 : 0;
-    }
-    if (is_numeric($offset)) {
-      // Support setting values via typed data objects.
-      if ($value instanceof TypedDataInterface) {
-        $value = $value->getValue();
-      }
-      $this->offsetGet($offset)->setValue($value);
-    }
-    else {
-      throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
-    }
-  }
-
-  /**
-   * Implements \IteratorAggregate::getIterator().
-   */
-  public function getIterator() {
-    if (isset($this->list)) {
-      return new \ArrayIterator($this->list);
-    }
-    return new \ArrayIterator(array());
-  }
-
-  /**
-   * Implements \Countable::count().
-   */
-  public function count() {
-    return isset($this->list) ? count($this->list) : 0;
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ListInterface::isEmpty().
-   */
-  public function isEmpty() {
-    if (isset($this->list)) {
-      foreach ($this->list as $item) {
-        if ($item instanceof ComplexDataInterface || $item instanceof ListInterface) {
-          if (!$item->isEmpty()) {
-            return FALSE;
-          }
-        }
-        // Other items are treated as empty if they have no value only.
-        elseif ($item->getValue() !== NULL) {
-          return FALSE;
-        }
-      }
-    }
-    return TRUE;
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ListInterface::onChange().
-   */
-  public function onChange($delta) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-  }
-
-  /**
-   * Magic method: Implements a deep clone.
-   */
-  public function __clone() {
-    if (isset($this->list)) {
-      foreach ($this->list as $delta => $item) {
-        $this->list[$delta] = clone $item;
-        $this->list[$delta]->setContext($delta, $this);
-      }
-    }
-  }
-}
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
new file mode 100644
index 0000000..34d99d0
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
@@ -0,0 +1,243 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Plugin\DataType\ItemList.
+ */
+
+namespace Drupal\Core\TypedData\Plugin\DataType;
+
+use Drupal\Core\TypedData\Annotation\DataType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * 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 {
+
+  /**
+   * Numerically indexed array items.
+   *
+   * @var array
+   */
+  protected $list = array();
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
+   */
+  public function getValue() {
+    if (isset($this->list)) {
+      $values = array();
+      foreach ($this->list as $delta => $item) {
+        $values[$delta] = $item->getValue();
+      }
+      return $values;
+    }
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
+   *
+   * @param array|null $values
+   *   An array of values of the field items, or NULL to unset the field.
+   */
+  public function setValue($values, $notify = TRUE) {
+    if (!isset($values) || $values === array()) {
+      $this->list = $values;
+    }
+    else {
+      if (!is_array($values)) {
+        throw new \InvalidArgumentException('Cannot set a list with a non-array value.');
+      }
+
+      // Clear the values of properties for which no value has been passed.
+      if (isset($this->list)) {
+        $this->list = array_intersect_key($this->list, $values);
+      }
+
+      // Set the values.
+      foreach ($values as $delta => $value) {
+        if (!is_numeric($delta)) {
+          throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
+        }
+        elseif (!isset($this->list[$delta])) {
+          $this->list[$delta] = $this->createItem($delta, $value);
+        }
+        else {
+          $this->list[$delta]->setValue($value);
+        }
+      }
+    }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getString().
+   */
+  public function getString() {
+    $strings = array();
+    if (isset($this->list)) {
+      foreach ($this->list as $item) {
+        $strings[] = $item->getString();
+      }
+      // Remove any empty strings resulting from empty items.
+      return implode(', ', array_filter($strings, 'drupal_strlen'));
+    }
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
+   */
+  public function getConstraints() {
+    if (isset($this->definition['item_definition']) && isset($this->definition['constraints'])) {
+      return $this->definition['constraints'];
+    }
+    // BC: If no item definition is specified, we apply possibly specified
+    // constraints to the list items only.
+    return array();
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetExists().
+   */
+  public function offsetExists($offset) {
+    return isset($this->list) && array_key_exists($offset, $this->list) && $this->offsetGet($offset)->getValue() !== NULL;
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetUnset().
+   */
+  public function offsetUnset($offset) {
+    if (isset($this->list)) {
+      unset($this->list[$offset]);
+    }
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetGet().
+   */
+  public function offsetGet($offset) {
+    if (!is_numeric($offset)) {
+      throw new \InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
+    }
+    // Allow getting not yet existing items as well.
+    // @todo: Maybe add a public createItem() method in addition?
+    elseif (!isset($this->list[$offset])) {
+      $this->list[$offset] = $this->createItem($offset);
+    }
+    return $this->list[$offset];
+  }
+
+  /**
+   * Helper for creating a list item object.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected function createItem($offset = 0, $value = NULL) {
+    return \Drupal::typedData()->getPropertyInstance($this, $offset, $value);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
+   */
+  public function getItemDefinition() {
+    if (isset($this->definition['item_definition'])) {
+      return $this->definition['item_definition'];
+    }
+    // For BC, fall back to generating an item definition from the list
+    // definition.
+    return array('list' => FALSE) + $this->definition;
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetSet().
+   */
+  public function offsetSet($offset, $value) {
+    if (!isset($offset)) {
+      // The [] operator has been used so point at a new entry.
+      $offset = $this->list ? max(array_keys($this->list)) + 1 : 0;
+    }
+    if (is_numeric($offset)) {
+      // Support setting values via typed data objects.
+      if ($value instanceof TypedDataInterface) {
+        $value = $value->getValue();
+      }
+      $this->offsetGet($offset)->setValue($value);
+    }
+    else {
+      throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
+    }
+  }
+
+  /**
+   * Implements \IteratorAggregate::getIterator().
+   */
+  public function getIterator() {
+    if (isset($this->list)) {
+      return new \ArrayIterator($this->list);
+    }
+    return new \ArrayIterator(array());
+  }
+
+  /**
+   * Implements \Countable::count().
+   */
+  public function count() {
+    return isset($this->list) ? count($this->list) : 0;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ListInterface::isEmpty().
+   */
+  public function isEmpty() {
+    if (isset($this->list)) {
+      foreach ($this->list as $item) {
+        if ($item instanceof ComplexDataInterface || $item instanceof ListInterface) {
+          if (!$item->isEmpty()) {
+            return FALSE;
+          }
+        }
+        // Other items are treated as empty if they have no value only.
+        elseif ($item->getValue() !== NULL) {
+          return FALSE;
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ListInterface::onChange().
+   */
+  public function onChange($delta) {
+    // Notify the parent of changes.
+    if (isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * Magic method: Implements a deep clone.
+   */
+  public function __clone() {
+    if (isset($this->list)) {
+      foreach ($this->list as $delta => $item) {
+        $this->list[$delta] = clone $item;
+        $this->list[$delta]->setContext($delta, $this);
+      }
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
index 96229b6..7b1ec1f 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
@@ -26,7 +26,9 @@ public function getType();
    * 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 5c48fb0..12d412d 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -82,6 +82,7 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
 
     // Allow per-data definition overrides of the used classes, i.e. take over
     // classes specified in the data definition.
+    // For BC, support definitions using the 'list' flag with 'list_class'.
     $key = empty($configuration['list']) ? 'class' : 'list_class';
     if (isset($configuration[$key])) {
       $class = $configuration[$key];
@@ -99,34 +100,9 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
   /**
    * 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.
@@ -151,9 +127,11 @@ 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) {
+  public function create($definition, $value = NULL, $name = NULL, $parent = NULL) {
+    if (is_object($definition)) {
+      $definition = $definition->toArray();
+    }
     $wrapper = $this->createInstance($definition['type'], $definition, $name, $parent);
     if (isset($value)) {
       $wrapper->setValue($value, FALSE);
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
index 1bb1ae4..64f4837 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
@@ -762,4 +762,18 @@ public function hasData() {
 
     return FALSE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldConfigurable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return TRUE;
+  }
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
index cbfb636..3034b8d 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
@@ -577,6 +577,20 @@ public function allowBundleRename() {
   /**
    * {@inheritdoc}
    */
+  public function isFieldConfigurable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function offsetExists($offset) {
     return (isset($this->{$offset}) || $offset == 'field_id' || $offset == 'field_name');
   }
