diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 7291e8d40a66eeb98f1bcdae47f82aeabeea063d..2488982410435b5fc4a173c461e96350382387ec 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -399,7 +399,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);
         }
@@ -437,7 +437,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);
       }
     }
@@ -710,7 +710,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;
       }
     }
@@ -724,7 +724,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]);
         }
@@ -779,7 +779,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();
@@ -911,7 +911,7 @@ public function __clone() {
         // object keyed by language. To avoid creating different field objects
         // we retain just the original value, as references will be recreated
         // later as needed.
-        if (empty($definitions[$name]['translatable']) && count($values) > 1) {
+        if (!$definitions[$name]->isFieldTranslatable() && count($values) > 1) {
           $values = array_intersect_key($values, array(Language::LANGCODE_DEFAULT => TRUE));
         }
         foreach ($values as $langcode => $items) {
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index 7759dcecaf5ad4dd2b62cc347d568aca0c6184c8..862b23fec57854147b81d71765cbcc5816f2e8b0 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -42,14 +42,20 @@ public function initTranslation($langcode);
   /**
    * Defines the base fields of the entity type.
    *
+   * Implementations typically use the class \Drupal\Core\Field\FieldDefinition
+   * for creating the field definitions; for example a 'name' field could be
+   * defined as the following:
+   * @code
+   * $fields['name'] = FieldDefinition::create('string')
+   *   ->setLabel(t('Name'));
+   * @endcode
+   *
    * @param string $entity_type
    *   The entity type to return properties for. Useful when a single class is
    *   used for multiple, possibly dynamic entity types.
    *
-   * @return array
-   *   An array of entity field definitions as specified by
-   *   \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions(), keyed by field
-   *   name.
+   * @return \Drupal\Core\Field\FieldDefinitionInterface[]
+   *   An array of entity field definitions, keyed by field name.
    *
    * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
    */
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 221d4ee3842f4620e03d775e508e0de7199c4625..2bce05d3af444764c9fa16eb90195253b298b42f 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\Field\FieldDefinition;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManager;
@@ -333,6 +334,8 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
         $this->entityFieldInfo[$entity_type] = $cache->data;
       }
       else {
+        // @todo: Refactor to allow for per-bundle overrides.
+        // See https://drupal.org/node/2114707.
         $class = $this->factory->getPluginClass($entity_type, $this->getDefinition($entity_type));
 
         $base_definitions = $class::baseFieldDefinitions($entity_type);
@@ -357,22 +360,31 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
         $result = $this->moduleHandler->invokeAll('entity_field_info', array($entity_type));
         $this->entityFieldInfo[$entity_type] = NestedArray::mergeDeep($this->entityFieldInfo[$entity_type], $result);
 
+        // Enforce field definitions to be objects.
+        foreach (array('definitions', 'optional') as $key) {
+          foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
+            if (is_array($definition)) {
+              $definition = FieldDefinition::createFromOldStyleDefinition($definition);
+            }
+            // Automatically set the field name for non-configurable fields.
+            if ($definition instanceof FieldDefinition) {
+              $definition->setFieldName($field_name);
+            }
+          }
+        }
+
+        // Invoke alter hooks.
         $hooks = array('entity_field_info', $entity_type . '_field_info');
         $this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type], $entity_type);
 
-        // Enforce fields to be multiple and untranslatable by default.
+        // Ensure all basic fields are not defined as translatable.
         $entity_info = $this->getDefinition($entity_type);
         $keys = array_intersect_key(array_filter($entity_info['entity_keys']), array_flip(array('id', 'revision', 'uuid', 'bundle')));
         $untranslatable_fields = array_flip(array('langcode') + $keys);
         foreach (array('definitions', 'optional') as $key) {
-          foreach ($this->entityFieldInfo[$entity_type][$key] as $name => &$definition) {
-            $definition['list'] = TRUE;
-            // Ensure ids and langcode fields are never made translatable.
-            if (isset($untranslatable_fields[$name]) && !empty($definition['translatable'])) {
-              throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition['label'])));
-            }
-            if (!isset($definition['translatable'])) {
-              $definition['translatable'] = FALSE;
+          foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
+            if (isset($untranslatable_fields[$field_name]) && $definition->isFieldTranslatable()) {
+              throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition->getFieldLabel())));
             }
           }
         }
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index e89966aa50b396f492fa2ab5ef03be136d4175de..fdc966618fb4acdf8049cbafa4b2376a3795ac17 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -25,9 +25,7 @@ public function getEntityTypeLabels();
   /**
    * Gets an array of content entity field definitions.
    *
-   * If a bundle is passed, fields specific to this bundle are included. Entity
-   * fields are always multi-valued, so 'list' is TRUE for each returned field
-   * definition.
+   * If a bundle is passed, fields specific to this bundle are included.
    *
    * @param string $entity_type
    *   The entity type to get field definitions for. Only entity types that
@@ -36,19 +34,11 @@ public function getEntityTypeLabels();
    *   (optional) The entity bundle for which to get field definitions. If NULL
    *   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.
+   * @return \Drupal\Core\Field\FieldDefinitionInterface[]
+   *   An array of entity field definitions, keyed by field name.
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
-   * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitionsByConstraints()
+   * @see \Drupal\Core\Entity\EntityManager::getFieldDefinitionsByConstraints()
    */
   public function getFieldDefinitions($entity_type, $bundle = NULL);
 
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index 5cf797340309c465716fac6780b0ce28f43c41cf..ec909d9904d60808b8b00aa95fa76f9b2366ec81 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/Field/ConfigFieldItemList.php b/core/lib/Drupal/Core/Field/ConfigFieldItemList.php
index 49e49d24107a872fae62940b76416aae5f18aab9..738df25975be0a0b9990fedf71ff11d198eabb50 100644
--- a/core/lib/Drupal/Core/Field/ConfigFieldItemList.php
+++ b/core/lib/Drupal/Core/Field/ConfigFieldItemList.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Field;
 
+use Drupal\Core\Field\Plugin\DataType\FieldInstanceInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\field\Field;
 
@@ -25,10 +26,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;
     }
   }
 
@@ -36,6 +38,10 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
+    // Configurable fields have the field_config entity injected as definition,
+    // but we want to return the more specific field instance here.
+    // @todo: Overhaul this once we have per-bundle field definitions injected,
+    // see https://drupal.org/node/2114707.
     if (!isset($this->instance)) {
       $entity = $this->getEntity();
       $instances = Field::fieldInfo()->getBundleInstances($entity->entityType(), $entity->bundle());
@@ -43,9 +49,7 @@ public function getFieldDefinition() {
         $this->instance = $instances[$this->getName()];
       }
       else {
-        // For base fields, fall back to the parent implementation.
-        // @todo: Inject the field definition with
-        //   https://drupal.org/node/2047229.
+        // For base fields, fall back to return the general definition.
         return parent::getFieldDefinition();
       }
     }
@@ -73,13 +77,6 @@ public function getConstraints() {
     return $constraints;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function getDefaultValue() {
-    return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php
index 8e57f7c47f064b81336afed927d1afdef5a7f02f..640bee33e3c8af0d9e1c2386fab17f75026ed894 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinition.php
@@ -8,27 +8,25 @@
 namespace Drupal\Core\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 {
+class FieldDefinition extends ListDefinition implements FieldDefinitionInterface, \ArrayAccess {
 
   /**
-   * The array holding values for all definition keys.
+   * Creates a new field definition.
    *
-   * @var array
-   */
-  protected $definition = array();
-
-  /**
-   * Constructs a new FieldDefinition object.
+   * @param string $type
+   *   The type of the field.
    *
-   * @param array $definition
-   *   (optional) If given, a definition represented as array.
+   * @return \Drupal\Core\Field\FieldDefinition
+   *   A new field definition object.
    */
-  public function __construct(array $definition = array()) {
-    $this->definition = $definition;
+  public static function create($type) {
+    return new static(array(), DataDefinition::create('field_item:' . $type));
   }
 
   /**
@@ -44,7 +42,7 @@ public function getFieldName() {
    * @param string $name
    *   The field name to set.
    *
-   * @return \Drupal\Core\Field\FieldDefinition
+   * @return self
    *   The object itself for chaining.
    */
   public function setFieldName($name) {
@@ -56,58 +54,63 @@ public function setFieldName($name) {
    * {@inheritdoc}
    */
   public function getFieldType() {
+    $data_type = $this->getItemDefinition()->getDataType();
     // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
-    $parts = explode(':', $this->definition['type']);
+    $parts = explode(':', $data_type);
     return $parts[1];
   }
 
   /**
-   * Sets the field type.
-   *
-   * @param string $type
-   *   The field type to set.
-   *
-   * @return \Drupal\Core\Field\FieldDefinition
-   *   The object itself for chaining.
+   * {@inheritdoc}
    */
-  public function setFieldType($type) {
-    $this->definition['type'] = 'field_item:' . $type;
-    return $this;
+  public function getFieldSettings() {
+    return $this->getItemDefinition()->getSettings();
   }
 
   /**
-   * Sets a field setting.
+   * Sets field settings.
    *
-   * @param string $type
-   *   The field type to set.
+   * @param array $settings
+   *   The value to set.
    *
-   * @return \Drupal\Core\Field\FieldDefinition
+   * @return self
    *   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 self
+   *   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());
   }
 
   /**
@@ -123,7 +126,7 @@ public function isFieldTranslatable() {
    * @param bool $translatable
    *   Whether the field is translatable.
    *
-   * @return \Drupal\Core\Field\FieldDefinition
+   * @return self
    *   The object itself for chaining.
    */
   public function setTranslatable($translatable) {
@@ -135,42 +138,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\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\Field\FieldDefinition
-   *   The object itself for chaining.
+   * {@inheritdoc}
    */
   public function setFieldDescription($description) {
-    $this->definition['description'] = $description;
-    return $this;
+    return $this->setDescription($description);
   }
 
   /**
@@ -185,7 +174,7 @@ public function getFieldCardinality() {
    * {@inheritdoc}
    */
   public function isFieldRequired() {
-    return !empty($this->definition['required']);
+    return $this->isRequired();
   }
 
   /**
@@ -200,13 +189,33 @@ public function isFieldMultiple() {
    * Sets whether the field is required.
    *
    * @param bool $required
-   *   TRUE if the field is required, FALSE otherwise.
+   *   Whether the field is required.
    *
-   * @return \Drupal\Core\Field\FieldDefinition
+   * @return self
    *   The object itself for chaining.
    */
   public function setFieldRequired($required) {
-    $this->definition['required'] = $required;
+    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 bool $queryable
+   *   Whether the field is queryable.
+   *
+   * @return self
+   *   The object itself for chaining.
+   */
+  public function setFieldQueryable($queryable) {
+    $this->definition['queryable'] = $queryable;
     return $this;
   }
 
@@ -218,11 +227,13 @@ public function setFieldRequired($required) {
    * @param array $constraints
    *   The constraints to set.
    *
-   * @return \Drupal\Core\Field\FieldDefinition
+   * @return self
    *   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;
   }
 
@@ -240,4 +251,77 @@ public function getFieldDefaultValue(EntityInterface $entity) {
     return $this->getFieldSetting('default_value');
   }
 
+  /**
+   * Allows creating field definition objects from old style definition arrays.
+   *
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  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']);
+      }
+    }
+    if (isset($definition['translatable'])) {
+      $field_definition->setTranslatable($definition['translatable']);
+      unset($definition['translatable']);
+    }
+
+    // Take care of the item definition now.
+    // Required applies to the field definition only.
+    unset($definition['required']);
+    $item_definition = new DataDefinition($definition);
+    $field_definition->setItemDefinition($item_definition);
+    return $field_definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  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}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  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/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
index bdaf247ea1f43c003742b1f0782039400ffff4a8..4dca17af3117cff6a32d8fe07087eb15e3034f6f 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Field;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\ListDefinitionInterface;
 
 /**
  * Defines an interface for entity field definitions.
@@ -36,10 +37,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\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 +51,7 @@
  * based on that abstract definition, even though that abstract definition can
  * differ from the concrete definition of any particular node's body field.
  */
-interface FieldDefinitionInterface {
+interface FieldDefinitionInterface extends ListDefinitionInterface {
 
   /**
    * Value indicating a field accepts an unlimited number of values.
@@ -130,9 +130,18 @@ public function isFieldTranslatable();
    * Determines whether the field is configurable via field.module.
    *
    * @return bool
+   *   TRUE if the field is configurable.
    */
   public function isFieldConfigurable();
 
+  /**
+   * Determines whether the field is queryable via QueryInterface.
+   *
+   * @return bool
+   *   TRUE if the field is queryable.
+   */
+  public function isFieldQueryable();
+
   /**
    * Returns the human-readable label for the field.
    *
@@ -148,8 +157,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/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php
index 31c9e09d82a7b7c1965306c67093507f64fb10ed..46459361d56a0b49843d3cf1e95494d55b842b3e 100644
--- a/core/lib/Drupal/Core/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Field/FieldItemBase.php
@@ -25,7 +25,7 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
   /**
    * 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/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 00dcabacf0f213f534be6c8e17cf358808964d41..d260668ba3d7336cd542ad6b61fd459faf944961 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -9,20 +9,16 @@
 
 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.
  *
- * 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.
- *
- * Supported settings (below the definition's 'settings' key) are:
- * - default_value: (optional) If set, the default value to apply to the field.
+ * An entity field is a list of field items, each containing a set of
+ * 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.
  */
 class FieldItemList extends ItemList implements FieldItemListInterface {
 
@@ -42,11 +38,10 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
   protected $langcode = Language::LANGCODE_DEFAULT;
 
   /**
-   * Overrides TypedData::__construct().
+   * {@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);
-    $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
@@ -79,7 +74,7 @@ public function getLangcode() {
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
-    return new FieldDefinition($this->definition);
+    return $this->definition;
   }
 
   /**
@@ -108,7 +103,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()) {
@@ -213,10 +208,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();
+
     // 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))) {
@@ -236,24 +229,7 @@ public function applyDefaultValue($notify = TRUE) {
    *   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;
+    return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php b/core/lib/Drupal/Core/Field/Plugin/DataType/Deriver/FieldItemDeriver.php
similarity index 94%
rename from core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php
rename to core/lib/Drupal/Core/Field/Plugin/DataType/Deriver/FieldItemDeriver.php
index 5e287f756d297157796509743522d4205853df95..d2fc7fdba379e78f0103758ba1c451f914b143e7 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php
+++ b/core/lib/Drupal/Core/Field/Plugin/DataType/Deriver/FieldItemDeriver.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver.
+ * Contains \Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver.
  */
 
-namespace Drupal\Core\Entity\Plugin\DataType\Deriver;
+namespace Drupal\Core\Field\Plugin\DataType\Deriver;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php b/core/lib/Drupal/Core/Field/Plugin/DataType/FieldItem.php
similarity index 61%
rename from core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php
rename to core/lib/Drupal/Core/Field/Plugin/DataType/FieldItem.php
index d97ea30ca2036b766d7f01e119ebee29dae4c70a..8f5c7d579c4102c018f2cee2d2f3630e12763be8 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/DataType/FieldItem.php
@@ -2,13 +2,11 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Plugin\DataType\FieldItem.
+ * Contains \Drupal\Core\Field\Plugin\DataType\FieldItem.
  */
 
-namespace Drupal\Core\Entity\Plugin\DataType;
+namespace Drupal\Core\Field\Plugin\DataType;
 
-use Drupal\Core\TypedData\Annotation\DataType;
-use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the base plugin for deriving data types for field types.
@@ -20,7 +18,7 @@
  *   id = "field_item",
  *   label = @Translation("Field item"),
  *   list_class = "\Drupal\Core\Field\FieldItemList",
- *   derivative = "Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver"
+ *   derivative = "Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver"
  * )
  */
 abstract class FieldItem {
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index 27ca9b23be15a1043f731c948c002d5603afcffe..82f088af649191e6c2a93af3b5d55a98717a4d5d 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'integer' entity field type.
@@ -31,15 +32,13 @@ class IntegerItem extends FieldItemBase {
   static $propertyDefinitions;
 
   /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
   public function getPropertyDefinitions() {
 
     if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'integer',
-        'label' => t('Integer value'),
-      );
+      static::$propertyDefinitions['value'] = DataDefinition::create('integer')
+        ->setLabel(t('Integer value'));
     }
     return static::$propertyDefinitions;
   }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LegacyConfigFieldItemList.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LegacyConfigFieldItemList.php
index 63889fdbfda33fe643e94b8b573a6d966024a37a..f239c60a0292ff85b1357f1fde474d94a175fdfc 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LegacyConfigFieldItemList.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/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::service('plugin.manager.field.field_type')->getDefinition($this->getFieldDefinition()->getFieldType());
+    $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/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index ab045f4c6c913ba2703acad5b8ed0e3df0f4962b..9c63b3334c62988e34f926b7f3a2dacbc37d0ef3 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'string' entity field type.
@@ -31,15 +32,13 @@ class StringItem extends FieldItemBase {
   static $propertyDefinitions;
 
   /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   * {@inheritdoc}
    */
   public function getPropertyDefinitions() {
 
     if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'string',
-        'label' => t('Text value'),
-      );
+      static::$propertyDefinitions['value'] = DataDefinition::create('string')
+        ->setLabel(t('Text value'));
     }
     return static::$propertyDefinitions;
   }
diff --git a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
index da2d300dae410e6d46ae4798d91cd62ea109b533..d47fb9e4a0a4aaaae4bf67a0cf1c50f83df6449f 100644
--- a/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
+++ b/core/lib/Drupal/Core/TypedData/Annotation/DataType.php
@@ -25,7 +25,7 @@
  *
  * 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
+ * type, is ordered and may contain duplicates. The class used for a list of
  * items of a certain type may be specified using the 'list class' key.
  *
  * @see \Drupal::typedData()
@@ -67,7 +67,7 @@ class DataType extends Plugin {
    *
    * @var string
    */
-  public $list_class = '\Drupal\Core\TypedData\ItemList';
+  public $list_class = '\Drupal\Core\TypedData\Plugin\DataType\ItemList';
 
   /**
    * The pre-defined primitive type that this data type maps to.
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 8acfaa312c8657cf3a14318b894d38c4573a2a39..2e3d0e766c4495b64d58a4b703c4f6a0886c089e 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 0000000000000000000000000000000000000000..a77aea41460c448957db576abc1ca37aa26244fe
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -0,0 +1,371 @@
+<?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.
+   *
+   * @param string $type
+   *   The data type of the data; e.g., 'string', 'integer' or 'any'.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinition
+   *   A new DataDefinition object.
+   */
+  public static function create($type) {
+    $definition['type'] = $type;
+    return new static($definition);
+  }
+
+  /**
+   * 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 self
+   *   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 self
+   *   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 self
+   *   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 bool $read_only
+   *   Whether the data is read-only.
+   *
+   * @return self
+   *   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 bool $computed
+   *   Whether the data is computed.
+   *
+   * @return self
+   *   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 bool $required
+   *   Whether the data is required.
+   *
+   * @return self
+   *   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 self
+   *   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 self
+   *   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 self
+   *   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 self
+   *   The object itself for chaining.
+   */
+  public function addConstraint($constraint_name, $options = NULL) {
+    $this->definition['constraints'][$constraint_name] = $options;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  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}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  public function &offsetGet($offset) {
+    if (!isset($this->definition[$offset])) {
+      $this->definition[$offset] = NULL;
+    }
+    return $this->definition[$offset];
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  public function offsetSet($offset, $value) {
+    $this->definition[$offset] = $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This is for BC support only.
+   * @todo: Remove once https://drupal.org/node/2112239 is in.
+   */
+  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 https://drupal.org/node/2112239 is in.
+   */
+  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 only.
+    unset($definition['required']);
+    $item_definition = new DataDefinition($definition);
+    $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 0000000000000000000000000000000000000000..eb5dd6767141212c0a19ab7926d4c285ced1901a
--- /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 bool
+   *   Whether the data is multi-valued.
+   */
+  public function isList();
+
+  /**
+   * Determines whether the data is read-only.
+   *
+   * @return bool
+   *   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 bool
+   *   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 bool
+   *   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 0000000000000000000000000000000000000000..231a0fc6ae286707a121044e8bde4a5941d4822c
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ListDefinition.php
@@ -0,0 +1,98 @@
+<?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.
+   *
+   * @param string $item_type
+   *   The data type of the list items; e.g., 'string', 'integer' or 'any'.
+   *
+   * @return \Drupal\Core\TypedData\ListDefinition
+   *   A new List Data Definition object.
+   */
+  public static function create($item_type) {
+    return new static(array(), DataDefinition::create($item_type));
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param
+   */
+  public function __construct(array $definition = array(), DataDefinitionInterface $item_definition = NULL) {
+    parent::__construct($definition);
+    $this->itemDefinition = isset($item_definition) ? $item_definition : DataDefinition::create('any');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataType() {
+    return 'list';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDataType($type) {
+    if ($type != 'list') {
+      throw new \LogicException('Lists must always be of data type "list".');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    $class = isset($this->definition['class']) ? $this->definition['class'] : NULL;
+    if (!empty($class)) {
+      return $class;
+    }
+    else {
+      // If a list definition is used but no class has been specified, derive
+      // the default list class from the item type.
+      $item_type_definition = \Drupal::typedData()
+        ->getDefinition($this->getItemDefinition()->getDataType());
+      return $item_type_definition['list_class'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getItemDefinition() {
+    return $this->itemDefinition;
+  }
+
+  /**
+   * Sets the item definition.
+   *
+   * @param \Drupal\Core\TypedData\DataDefinition $definition
+   *   A list item's data definition.
+   *
+   * @return self
+   *   The object itself for chaining.
+   */
+  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 0000000000000000000000000000000000000000..c07d60e90134b3afa270b5fec3ded893bd76435e
--- /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 2088c2d9f784ea89f38b749581d40ea0c291b75e..53f5eac68d9c44e48c0a23efae3cd2e655a7580f 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -11,7 +11,7 @@
  * Interface for a list of typed data.
  *
  * A list of typed data contains only items of the same type, is ordered and may
- * contain duplicates.
+ * contain duplicates. Note that the data type of a list is always 'list'.
  *
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
@@ -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 90%
rename from core/lib/Drupal/Core/TypedData/ItemList.php
rename to core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
index 9b39e83b60cabb304b904fd27f25ee0cc536eb2f..89d2cd8584d2a722cd42cdca12fb168282d9e008 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\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.
+ * This class can serve as list for any type of items and is used by default.
+ * Data types may specify the default list class in their definition, see
+ * Drupal\Core\TypedData\Annotation\DataType.
  * 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 {
 
@@ -88,14 +100,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().
    */
@@ -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 430ed2a5bcaa8f5db20ff77e928e3815dc06f216..6597f3cdd09dd51684a2a32e2c1ec07eea0ecb11 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -20,7 +20,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
   /**
    * The data definition.
    *
-   * @var array
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface
    */
   protected $definition;
 
@@ -41,7 +41,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
   /**
    * 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
@@ -51,8 +51,12 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
    *   root of a typed data tree. Defaults to NULL.
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
+   *
+   * @todo When \Drupal\Core\Config\TypedConfigManager has been fixed to use
+   *   class-based definitions, type-hint $definition to
+   *   DataDefinitionInterface.
    */
-  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 bbdf4ead199f774e3b0f49f797e63ebe4b4b71e1..4d7b0661e57616f45e80874b740ef1b26f3dee95 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
@@ -17,8 +17,8 @@ interface TypedDataInterface {
   /**
    * Gets the data definition.
    *
-   * @return array
-   *   The data definition array.
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   *   The data definition object.
    */
   public function getDefinition();
 
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 1428822701ae6701e5fe7bd29ae311a516e76b2a..7e308125d162637797a786c66ecd273c700a8be3 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -52,77 +52,49 @@ 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];
-    }
+    // classes specified in the type definition.
+    $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.
+   *   @todo: Need to remove array support in https://drupal.org/node/2112239.
    * @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.
@@ -148,12 +120,20 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
    * @see \Drupal\Core\TypedData\Plugin\DataType\Uri
    * @see \Drupal\Core\TypedData\Plugin\DataType\Binary
    */
-  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) {
+    // @todo: Remove array support once https://drupal.org/node/2112239 is in.
+    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;
   }
 
   /**
@@ -335,18 +315,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')) {
@@ -363,19 +345,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 ba827e6c6da7b77237880ef53fb1e2947f4a7133..647188d63f1e8a46dcc823b1cb654fd1d134571b 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/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc
index f621b2f240ccaafcdb9646968b9a6d43a444ca0c..495b00600236f1a271a540262a8b75e9d31cdd8d 100644
--- a/core/modules/content_translation/content_translation.admin.inc
+++ b/core/modules/content_translation/content_translation.admin.inc
@@ -101,7 +101,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
             // translation.
             // @todo Remove this special casing as soon as configurable and
             //   base field definitions are "unified".
-            if (!empty($definition['configurable']) && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
+            if ($definition->isFieldConfigurable() && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
               $instance = FieldService::fieldInfo()->getInstance($entity_type, $bundle, $field_name);
               $form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
                 '#label' => $instance->getFieldLabel(),
@@ -122,7 +122,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
             // fields support translation. Whether they are actually enabled is
             // determined through our settings. As a consequence only fields
             // that support translation can be enabled or disabled.
-            elseif (isset($field_settings[$field_name]) || !empty($definition['translatable'])) {
+            elseif (isset($field_settings[$field_name]) || $definition->isFieldTranslatable()) {
               $form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
                 '#label' => $definition['label'],
                 '#type' => 'checkbox',
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index cb950820deafb6506b5c96088343c7f0e6b9c578..66b6d66fba5a952a8702229ca57fddc7de549113 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -134,7 +134,7 @@ function content_translation_entity_field_info_alter(&$info, $entity_type) {
     foreach ($fields as $name => $translatable) {
       foreach ($keys as $key) {
         if (isset($info[$key][$name])) {
-          $info[$key][$name]['translatable'] = (bool) $translatable;
+          $info[$key][$name]->setTranslatable((bool) $translatable);
           break;
         }
       }
@@ -655,7 +655,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
     if (!$entity->isNew() && (!isset($translations[$form_langcode]) || count($translations) > 1)) {
       foreach ($entity->getPropertyDefinitions() as $property_name => $definition) {
         if (isset($form[$property_name])) {
-          $form[$property_name]['#multilingual'] = !empty($definition['translatable']);
+          $form[$property_name]['#multilingual'] = $definition->isFieldTranslatable();
         }
       }
     }
diff --git a/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php b/core/modules/datetime/lib/Drupal/datetime/DateTimeComputed.php
index c0f612398bff5ae1361168679627a64af9e9d05e..d6954b944a4c19e0c492a96f25e60d6c13c43fc9 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 02ead20e3161853d4ed1fd3013c4835e1a9022c5..d72e0d1623113c3a98280e3b88b7715f45543385 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -189,7 +189,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-field-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 7951f27ed8f387d2abcbdb23d2f760842ae8d4b9..07c174f6d5e7bc3218c4f17f30a87857fa0e9630 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -557,7 +557,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 120b754a9b62f62ea9993849ec2b50f34282e27b..6461ec445342722bad29c4c4f27597155d50f9de 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -27,35 +27,6 @@ function entity_reference_field_info_alter(&$info) {
   $info['entity_reference']['list_class'] = '\Drupal\entity_reference\Plugin\Field\FieldType\ConfigurableEntityReferenceFieldItemList';
 }
 
-/**
- * Implements hook_entity_field_info_alter().
- *
- * Set the "target_type" and "list_class" property definition for entity
- * reference fields.
- *
- * @see \Drupal\Core\Field\Plugin\Field\FieldType\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().
  */
diff --git a/core/modules/field/field.deprecated.inc b/core/modules/field/field.deprecated.inc
index 8ac2bc2c7b76b46c73f40585421e3624d00ca176..86fa4606520d8a528961d76df544059acbd5508e 100644
--- a/core/modules/field/field.deprecated.inc
+++ b/core/modules/field/field.deprecated.inc
@@ -616,7 +616,7 @@ function field_attach_form_validate(ContentEntityInterface $entity, $form, &$for
   $has_violations = FALSE;
   foreach ($entity as $field_name => $field) {
     $definition = $field->getDefinition();
-    if (!empty($definition['configurable']) && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
+    if ($definition->isFieldConfigurable() && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
       $field_violations = $field->validate();
       if (count($field_violations)) {
         $has_violations = TRUE;
@@ -926,13 +926,13 @@ function field_language(EntityInterface $entity, $field_name = NULL, $langcode =
   if (!isset($field_name)) {
     $display_langcodes = array();
     foreach ($definitions as $name => $definition) {
-      if (!empty($definition['configurable'])) {
+      if ($definition->isFieldConfigurable()) {
         $display_langcodes[$name] = $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
       }
     }
     return $display_langcodes;
   }
-  elseif (!empty($definitions[$field_name]['configurable'])) {
+  elseif ($definitions[$field_name]->isFieldConfigurable()) {
     return $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
   }
 }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 0d56a0a7d98733d8cfbe43967eda2ca0ba1ad616..91464f08341bf7c722715887c7e5ee863c204877 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -194,16 +194,17 @@ 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() to allow per-bundle field
+    // definitions, such that we can pass on field instances as field
+    // definitions here. See https://drupal.org/node/2114707.
 
     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();
       }
     }
   }
@@ -211,33 +212,6 @@ function field_entity_field_info($entity_type) {
   return $property_info;
 }
 
-/**
- * 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_entity_bundle_create().
  */
diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php
index 960dc957bba33d8125b7f9721a3d896035e3e71d..fc5234660a8bceb24c9e8db3246264919c905d01 100644
--- a/core/modules/field/lib/Drupal/field/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\field\FieldException;
 use Drupal\field\FieldInterface;
 
@@ -197,6 +198,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.
    *
@@ -566,6 +574,20 @@ public function isFieldTranslatable() {
     return $this->translatable;
   }
 
+  /**
+   * Sets whether the field is translatable.
+   *
+   * @param bool $translatable
+   *   Whether the field is translatable.
+   *
+   * @return \Drupal\field\Entity\Field
+   *   The object itself for chaining.
+   */
+  public function setTranslatable($translatable) {
+    $this->translatable = $translatable;
+    return $this;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -577,7 +599,7 @@ public function getFieldLabel() {
    * {@inheritdoc}
    */
   public function getFieldDescription() {
-    return '';
+    return NULL;
   }
 
   /**
@@ -607,6 +629,20 @@ public function isFieldMultiple() {
    */
   public function getFieldDefaultValue(EntityInterface $entity) { }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldConfigurable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldQueryable() {
+    return TRUE;
+  }
+
   /**
    * A list of columns that can not be used as field type columns.
    *
@@ -675,8 +711,90 @@ public function __wakeup() {
   /**
    * {@inheritdoc}
    */
-  public function isFieldConfigurable() {
+  public function getDataType() {
+    return '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() {
+    // Derive list class from the field type.
+    $type_definition = \Drupal::service('plugin.manager.field.field_type')
+      ->getDefinition($this->getFieldType());
+    return $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('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 6ad3f86c3ae62c0e17573c61e9449cc5096c94cc..7e34d3d5d9cd62f99a54c204fc89fe2e440920dd 100644
--- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\field\FieldException;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\field\FieldInstanceInterface;
 
 /**
@@ -206,6 +207,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.
    *
@@ -566,17 +574,24 @@ public function getFieldDefaultValue(EntityInterface $entity) {
   /**
    * {@inheritdoc}
    */
-  public function allowBundleRename() {
-    $this->bundle_rename_allowed = TRUE;
+  public function isFieldConfigurable() {
+    return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function isFieldConfigurable() {
+  public function isFieldQueryable() {
     return TRUE;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function allowBundleRename() {
+    $this->bundle_rename_allowed = TRUE;
+  }
+
   /*
    * Implements the magic __sleep() method.
    *
@@ -598,4 +613,82 @@ public function __wakeup() {
     $this->__construct($values);
   }
 
+  public function getDataType() {
+    return '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() {
+    return $this->field->getClass();
+  }
+
+  /**
+   * {@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('field_item:' . $this->field->type)
+        ->setSettings($this->getFieldSettings());
+    }
+    return $this->itemDefinition;
+  }
 }
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
index 17aacc0977c3c02cb79e24e46b08c07885aa8b8f..593a501f8d2f1b187ba94f705aee704a2d7747ba 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
@@ -64,8 +64,8 @@ public function normalize($field_item, $format = NULL, array $context = array())
    */
   protected function constructValue($data, $context) {
     $field_item = $context['target_instance'];
-    $field_definition = $field_item->getDefinition();
-    $target_type = $field_definition['settings']['target_type'];
+    $field_definition = $field_item->getFieldDefinition();
+    $target_type = $field_definition->getFieldSetting('target_type');
     if ($id = $this->entityResolver->resolve($this, $data, $target_type)) {
       return array('target_id' => $id);
     }
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
index 008f20e940434531b3f0c645363d12902fa4d2d6..2d60eb67c4cdb2594872442ec2e82b29d9ca405c 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
@@ -57,8 +57,8 @@ public function denormalize($data, $class, $format = NULL, array $context = arra
     if (isset($data['lang'])) {
       $langcode = $data['lang'];
       unset($data['lang']);
-      $field_definition = $field_item->getDefinition();
-      if ($field_definition['translatable']) {
+      $field_definition = $field_item->getFieldDefinition();
+      if ($field_definition->isFieldTranslatable()) {
         $field_item = $this->createTranslatedInstance($field_item, $langcode);
       }
     }
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php
index bd3472f0bcdfca32ac40e6781f1ae060e81fec81..b433c79a5134eadce9fddd46bfd8ed10d3d57e84 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php
@@ -33,11 +33,11 @@ public function normalize($field, $format = NULL, array $context = array()) {
     // Get the field definition.
     $entity = $field->getEntity();
     $field_name = $field->getName();
-    $field_definition = $entity->getPropertyDefinition($field_name);
+    $field_definition = $field->getFieldDefinition();
 
     // If this field is not translatable, it can simply be normalized without
     // separating it into different translations.
-    if (empty($field_definition['translatable'])) {
+    if (!$field_definition->isFieldTranslatable()) {
       $normalized_field_items = $this->normalizeFieldItems($field, $format, $context);
     }
     // Otherwise, the languages have to be extracted from the entity and passed
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
index 5d6b20060c03de2fd7da14ce2e537fe998c0e2de..7be07a9fd3a07f0e2299ec9b988787461e2ae3c6 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
@@ -41,7 +41,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 +50,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/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php b/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/ListNormalizerTest.php
index f61c67592596c1f96a5f408a4dec03cd57666383..6c8315d93606b79d11ec746822c5384308e24ac4 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/entity.api.php b/core/modules/system/entity.api.php
index 05f9e57bd0b5829c8995f1b0f2b38de1fe7cea54..f87f5a33aa544548de5d09bb36bd340b4843bfa4 100644
--- a/core/modules/system/entity.api.php
+++ b/core/modules/system/entity.api.php
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Field\FieldDefinition;
 
 /**
  * @addtogroup hooks
@@ -626,10 +627,8 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
  *
  * @return array
  *   An array of entity field information having the following optional entries:
- *   - definitions: An array of field definitions to add all entities of this
- *     type, keyed by field name. See
- *     \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions() for a list of
- *     supported keys in field definitions.
+ *   - definitions: An array of field definitions to add to all entities of this
+ *     type, keyed by field name.
  *   - optional: An array of field definitions for optional entity fields, keyed
  *     by field name. Optional fields are fields that only exist for certain
  *     bundles of the entity type.
@@ -637,29 +636,26 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
  *     optional fields that entities of this bundle have.
  *
  * @see hook_entity_field_info_alter()
+ * @see \Drupal\Core\Field\FieldDefinitionInterface
  * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
  * @see \Drupal\Core\TypedData\TypedDataManager::create()
  */
 function hook_entity_field_info($entity_type) {
   if (mymodule_uses_entity_type($entity_type)) {
     $info = array();
-    $info['definitions']['mymodule_text'] = array(
-      'type' => 'string_item',
-      'list' => TRUE,
-      'label' => t('The text'),
-      'description' => t('A text property added by mymodule.'),
-      'computed' => TRUE,
-      'class' => '\Drupal\mymodule\EntityComputedText',
-    );
+    $info['definitions']['mymodule_text'] = FieldDefinition::create('string')
+      ->setLabel(t('The text'))
+      ->setDescription(t('A text property added by mymodule.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\mymodule\EntityComputedText');
+
     if ($entity_type == 'node') {
       // Add a property only to entities of the 'article' bundle.
-      $info['optional']['mymodule_text_more'] = array(
-        'type' => 'string_item',
-        'list' => TRUE,
-        'label' => t('More text'),
-        'computed' => TRUE,
-        'class' => '\Drupal\mymodule\EntityComputedMoreText',
-      );
+      $info['optional']['mymodule_text_more'] = FieldDefinition::create('string')
+        ->setLabel(t('More text'))
+        ->setComputed(TRUE)
+        ->setClass('\Drupal\mymodule\EntityComputedMoreText');
+
       $info['bundle map']['article'][0] = 'mymodule_text_more';
     }
     return $info;
@@ -678,8 +674,8 @@ function hook_entity_field_info($entity_type) {
  */
 function hook_entity_field_info_alter(&$info, $entity_type) {
   if (!empty($info['definitions']['mymodule_text'])) {
-    // Alter the mymodule_text property to use a custom class.
-    $info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
+    // Alter the mymodule_text field to use a custom class.
+    $info['definitions']['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
   }
 }
 
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 296c4291a9e1834f047ef6719458d40df306b93b..14cda3c62cf2e19f62ac2df690604914b3e2d4d6 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,19 @@ 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'], 'field_item:string', $entity_type .': Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $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'], 'field_item:string', $entity_type .': Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $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 8258a4020f907b6ae321696635cfa6c65099877e..4de69ddaac3b2a68225d06f464f9a6e394170094 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -7,7 +7,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 +313,7 @@ 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);
+    $typed_data = $this->createTypedData(ListDefinition::create('string'), $value);
     $this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
     // Test iterating.
     $count = 0;
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 235076616572789d1c1ddf2c3473fd1a5b9c5ccb..9c2b0b986b03d55bbeb87aae1d7b49ab65697015 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -75,7 +75,7 @@ function entity_test_entity_info_alter(&$info) {
 function entity_test_entity_field_info_alter(&$info, $entity_type) {
   if ($entity_type == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
     foreach ($names as $name => $value) {
-      $info['definitions'][$name]['translatable'] = $value;
+      $info['definitions'][$name]->setTranslatable($value);
     }
   }
 }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
index a091d1a18a62b117e6c19e773412c7f6ec3b7916..798763590b7831273152c9ec88f320a115610227 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\Field\FieldDefinition;
 use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\DataDefinition;
 use Drupal\taxonomy\TermInterface;
 
 /**
@@ -200,67 +202,55 @@ 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('integer')
+      ->setLabel(t('Term ID'))
+      ->setDescription(t('The term ID.'))
+      ->setReadOnly(TRUE);
+
+    $fields['uuid'] = FieldDefinition::create('uuid')
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The term UUID.'))
+      ->setReadOnly(TRUE);
+
+    $fields['vid'] = FieldDefinition::create('string')
+      ->setLabel(t('Vocabulary ID'))
+      ->setDescription(t('The ID of the vocabulary to which the term is assigned.'));
+
+    $fields['langcode'] = FieldDefinition::create('language')
+      ->setLabel(t('Language code'))
+      ->setDescription(t('The term language code.'));
+
+    $fields['name'] = FieldDefinition::create('string')
+      ->setLabel(t('Name'))
+      ->setDescription(t('The term name.'));
+
+    $fields['description'] = FieldDefinition::create('string')
+      ->setLabel(t('Description'))
+      ->setDescription(t('A description of the term.'));
+
     // @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('string')
+      ->setLabel(t('Description format'))
+      ->setDescription(t('The filter format ID of the description.'));
+
+    $fields['weight'] = FieldDefinition::create('integer')
+      ->setLabel(t('Weight'))
+      ->setDescription(t('The weight of this term in relation to other terms.'))
+      ->setFieldSetting('default_value', 0);
+
+    $fields['parent'] = FieldDefinition::create('integer')
+      ->setLabel(t('Term Parents'))
+      ->setDescription(t('The parents of this term.'))
       // 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('integer')
+      ->setLabel(t('Changed'))
+      ->setDescription(t('The time that the term was last edited.'))
+      ->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 bbf0fd01684db65c07eb580ce2dcfbcd05559e26..dd1fe8e40827e0bc7fa02c2161d7d49b436ff49a 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 3d60febf3640c9a19df1e88558b00a5fb8c9ffb4..5dfc084b773568add636bccf02034c4c69879d81 100644
--- a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
@@ -25,6 +25,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: Overhaul how field definitions deal with dependencies and improve
+    // unit tests. See https://drupal.org/node/2143555.
+    $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.
    */
@@ -59,10 +87,9 @@ public function testFieldDescription() {
    * Tests field type methods.
    */
   public function testFieldType() {
-    $definition = new FieldDefinition();
-    $field_name = $this->randomName();
-    $definition->setFieldType($field_name);
-    $this->assertEquals($field_name, $definition->getFieldType());
+    $field_type = $this->randomName();
+    $definition = FieldDefinition::create($field_type);
+    $this->assertEquals($field_type, $definition->getFieldType());
   }
 
   /**