diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
index 8becdd7..73e9402 100644
--- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
+++ b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
@@ -6,12 +6,16 @@
  */
 
 namespace Drupal\Core\Config\Schema;
+
+use Drupal\Component\Utility\String;
 use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
  * Defines a generic configuration element that contains multiple properties.
  */
-abstract class ArrayElement extends Element implements \IteratorAggregate, TraversableTypedDataInterface, \ArrayAccess, \Countable {
+abstract class ArrayElement extends TypedData implements \IteratorAggregate, TraversableTypedDataInterface {
 
   /**
    * Parsed elements.
@@ -19,19 +23,6 @@
   protected $elements;
 
   /**
-   * Gets an array of contained elements.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface[]
-   *   An array of elements contained in this element.
-   */
-  protected function getElements() {
-    if (!isset($this->elements)) {
-      $this->elements = $this->parse();
-    }
-    return $this->elements;
-  }
-
-  /**
    * Gets valid configuration data keys.
    *
    * @return array
@@ -47,59 +38,91 @@ protected function getAllKeys() {
    * @return \Drupal\Core\TypedData\TypedDataInterface[]
    *   An array of elements contained in this element.
    */
-  protected abstract function parse();
+  protected function parse() {
+    $elements = array();
+    foreach ($this->getAllKeys() as $key) {
+      $value = isset($this->value[$key]) ? $this->value[$key] : NULL;
+      $definition = $this->getElementDefinition($key);
+      $elements[$key] = $this->createElement($definition, $value, $key);
+    }
+    return $elements;
+  }
 
   /**
-   * Implements TypedDataInterface::validate().
+   * {@inheritdoc}
    */
-  public function validate() {
-    foreach ($this->getElements() as $element) {
-      if (!$element->validate()) {
-        return FALSE;
+  public function get($name) {
+    $parts = explode('.', $name);
+    $root_key = array_shift($parts);
+    $elements = $this->getElements();
+    if (isset($elements[$root_key])) {
+      $element = $elements[$root_key];
+      // If $property_name contained a dot recurse into the keys.
+      while ($element && ($key = array_shift($parts)) !== NULL) {
+        if ($element instanceof TraversableTypedDataInterface) {
+          $element = $element->get($key);
+        }
+        else {
+          $element = NULL;
+        }
       }
     }
-    return TRUE;
+    if (isset($element)) {
+      return $element;
+    }
+    else {
+      throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $name)));
+    }
   }
 
   /**
-   * Implements ArrayAccess::offsetExists().
+   * {@inheritdoc}
    */
-  public function offsetExists($offset) {
-    return array_key_exists($offset, $this->getElements());
+  public function set($key, $value, $notify = TRUE) {
+    // Support setting values via typed data objects.
+    if ($value instanceof TypedDataInterface) {
+      $value = $value->getValue();
+    }
+    $this->value[$key] = $value;
+    // Parsed elements must be rebuilt with new values
+    unset($this->elements);
+    // Directly notify ourselves.
+    $this->onChange($key, $value);
+    return $this;
   }
 
   /**
-   * Implements ArrayAccess::offsetGet().
+   * {@inheritdoc}
    */
-  public function offsetGet($offset) {
-    $elements = $this->getElements();
-    return $elements[$offset];
+  public function getElements($include_computed = FALSE) {
+    if (!isset($this->elements)) {
+      $this->elements = $this->parse();
+    }
+    return $this->elements;
   }
 
   /**
-   * Implements ArrayAccess::offsetSet().
+   * {@inheritdoc}
    */
-  public function offsetSet($offset, $value) {
-    if ($value instanceof TypedDataInterface) {
-      $value = $value->getValue();
-    }
-    $this->value[$offset] = $value;
-    unset($this->elements);
+  public function isEmpty() {
+    return empty($this->value);
   }
 
   /**
-   * Implements ArrayAccess::offsetUnset().
+   * {@inheritdoc}
    */
-  public function offsetUnset($offset) {
-    unset($this->value[$offset]);
-    unset($this->elements);
+  public function toArray() {
+    return isset($this->value) ? $this->value : array();
   }
 
   /**
-   * Implements Countable::count().
+   * {@inheritdoc}
    */
-  public function count() {
-    return count($this->getElements());
+  public function onChange($name) {
+    // Notify the parent of changes.
+    if (isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
@@ -109,4 +132,39 @@ public function getIterator() {
     return new \ArrayIterator($this->getElements());
   }
 
+  /**
+   * Creates a contained typed configuration object.
+   *
+   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
+   *   The data definition object.
+   * @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.
+   * @param string $name
+   *   The key of the contained element.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected function createElement($definition, $value, $key) {
+    return \Drupal::service('config.typed')->create($definition, $value, $key, $this);
+  }
+
+  /**
+   * Creates a new data definition object from a type definition array and
+   * actual configuration data.
+   *
+   * @param array $definition
+   *   The base type definition array, for which a data definition should be
+   *   created.
+   * @param $value
+   *   The value of the configuration element.
+   * @param string $key
+   *   The key of the contained element.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   */
+  protected function buildDataDefinition($definition, $value, $key) {
+    return  \Drupal::service('config.typed')->buildDataDefinition($definition, $value, $key, $this);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php
deleted file mode 100644
index c492d42..0000000
--- a/core/lib/Drupal/Core/Config/Schema/Element.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Config\Schema\Element.
- */
-
-namespace Drupal\Core\Config\Schema;
-
-use Drupal\Core\TypedData\TypedData;
-
-/**
- * Defines a generic configuration element.
- */
-abstract class Element extends TypedData {
-
-  /**
-   * The configuration value.
-   *
-   * @var mixed
-   */
-  protected $value;
-
-  /**
-   * Create typed config object.
-   */
-  protected function parseElement($key, $data, $definition) {
-    return \Drupal::service('config.typed')->create($definition, $data, $key, $this);
-  }
-
-  /**
-   * Build data definition object for contained elements.
-   *
-   * @return \Drupal\Core\TypedData\DataDefinitionInterface
-   */
-  protected function buildDataDefinition($definition, $value, $key) {
-    return  \Drupal::service('config.typed')->buildDataDefinition($definition, $value, $key, $this);
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Config/Schema/Ignore.php b/core/lib/Drupal/Core/Config/Schema/Ignore.php
index 20701b8..22556f8 100644
--- a/core/lib/Drupal/Core/Config/Schema/Ignore.php
+++ b/core/lib/Drupal/Core/Config/Schema/Ignore.php
@@ -7,15 +7,11 @@
 
 namespace Drupal\Core\Config\Schema;
 
+use Drupal\Core\TypedData\TypedData;
+
 /**
  * Configuration property to ignore.
  */
-class Ignore extends Element {
+class Ignore extends TypedData {
 
-  /**
-   * {@inheritdoc}.
-   */
-  public function validate() {
-    return TRUE;
-  }
 }
diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php
index eed1da8..e63d2e3 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -7,151 +7,28 @@
 
 namespace Drupal\Core\Config\Schema;
 
-use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Component\Utility\String;
-
 /**
  * Defines a mapping configuration element.
  *
- * Wraps configuration data and metadata allowing access to configuration data
- * using the ComplexDataInterface API. This object may contain any number and
- * type of nested properties.
+ * This object may contain any number and type of nested properties and each
+ * property key may have its own definition in the 'mapping' property of the
+ * configuration schema.
+ *
+ * Properties in the configuration value that are not defined in the mapping
+ * will get the 'undefined' data type.
+ *
+ * Read https://drupal.org/node/1905070 for more details about configuration
+ * schema, types and type resolution.
  */
-class Mapping extends ArrayElement implements ComplexDataInterface {
-
-  /**
-   * An array of data definitions.
-   *
-   * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
-   */
-  protected $propertyDefinitions;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function parse() {
-    $elements = array();
-    foreach ($this->getPropertyDefinitions() as $key => $definition) {
-      $elements[$key] = $this->parseElement($key, $this->value[$key], $definition);
-    }
-    return $elements;
-  }
-
-  /**
-   * {@inheritdoc}
-   *
-   * Since all configuration objects are mappings the function will except a dot
-   * delimited key to access nested values, for example, 'page.front'.
-   */
-  public function get($property_name) {
-    $parts = explode('.', $property_name);
-    $root_key = array_shift($parts);
-    $elements = $this->getElements();
-    if (isset($elements[$root_key])) {
-      $element = $elements[$root_key];
-      // If $property_name contained a dot recurse into the keys.
-      while ($element && ($key = array_shift($parts)) !== NULL) {
-        if (method_exists($element, 'get')) {
-          $element = $element->get($key);
-        }
-        else {
-          $element = NULL;
-        }
-      }
-    }
-    if (isset($element)) {
-      return $element;
-    }
-    else {
-      throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name)));
-    }
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::set().
-   */
-  public function set($property_name, $value, $notify = TRUE) {
-    // Set the data into the configuration array but behave according to the
-    // interface specification when we've got a null value.
-    if (isset($value)) {
-      $this->value[$property_name] = $value;
-      $property = $this->get($property_name);
-    }
-    else {
-      // In these objects, when clearing the value, the property is gone.
-      // As this needs to return a property, we get it before we delete it.
-      $property = $this->get($property_name);
-      unset($this->value[$property_name]);
-      $property->setValue($value);
-    }
-    // Notify the parent of any changes.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-    return $this;
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties().
-   */
-  public function getProperties($include_computed = FALSE) {
-    return $this->getElements();
-  }
+class Mapping extends ArrayElement {
 
   /**
    * {@inheritdoc}
    */
-  public function toArray() {
-    return $this->getValue();
-  }
-
-  /**
-   * Gets the definition of a contained property.
-   *
-   * @param string $name
-   *   The name of property.
-   *
-   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
-   *   The definition of the property or NULL if the property does not exist.
-   */
-  public function getPropertyDefinition($name) {
-    $definitions = $this->getPropertyDefinitions();
-    return isset($definitions[$name]) ? isset($definitions[$name]) : NULL;
-  }
-
-  /**
-   * Gets an array of property definitions of contained properties.
-   *
-   * @return \Drupal\Core\TypedData\DataDefinitionInterface[]
-   *   An array of property definitions of contained properties, keyed by
-   *   property name.
-   */
-  public function getPropertyDefinitions() {
-    if (!isset($this->propertyDefinitions)) {
-      $this->propertyDefinitions = array();
-      foreach ($this->getAllKeys() as $key) {
-        $definition = isset($this->definition['mapping'][$key]) ? $this->definition['mapping'][$key] : array();
-        $this->propertyDefinitions[$key] = $this->buildDataDefinition($definition, $this->value[$key], $key);
-      }
-    }
-    return $this->propertyDefinitions;
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
-   */
-  public function isEmpty() {
-    return empty($this->value);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange().
-   */
-  public function onChange($property_name) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
+  public function getElementDefinition($key) {
+    $value = isset($this->value[$key]) ? $this->value[$key] : NULL;
+    $definition = isset($this->definition['mapping'][$key]) ? $this->definition['mapping'][$key] : array();
+    return $this->buildDataDefinition($definition, $value, $key);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php
index bce81a9..4190330 100644
--- a/core/lib/Drupal/Core/Config/Schema/Sequence.php
+++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php
@@ -8,85 +8,35 @@
 namespace Drupal\Core\Config\Schema;
 
 use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\ListTrait;
 
 /**
  * Defines a configuration element of type Sequence.
+ *
+ * This object may contain any number and type of nested elements that share
+ * a common definition in the 'sequence' property of the configuration schema.
+ *
+ * Read https://drupal.org/node/1905070 for more details about configuration
+ * schema, types and type resolution.
  */
 class Sequence extends ArrayElement implements ListInterface {
 
-  /**
-   * Data definition
-   *
-   * @var \Drupal\Core\TypedData\DataDefinitionInterface
-   */
-  protected $itemDefinition;
+  use ListTrait;
 
   /**
    * {@inheritdoc}
    */
-  protected function parse() {
-    // Creates a new data definition object for each item from the generic type
-    // definition array and actual configuration data for that item. Type
-    // definitions may contain variables to be replaced and those depend on
-    // each item's data.
-    $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array();
-    $elements = array();
-    foreach ($this->value as $key => $value) {
-      $data_definition =  $this->buildDataDefinition($definition, $value, $key);
-      $elements[$key] = $this->parseElement($key, $value, $data_definition);
-    }
-    return $elements;
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ListInterface::isEmpty().
-   */
-  public function isEmpty() {
-    return empty($this->value);
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ListInterface::getItemDefinition().
-   */
   public function getItemDefinition() {
-    if (!isset($this->itemDefinition)) {
-      $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array();
-      $this->itemDefinition = $this->buildDataDefinition($definition, NULL);
-    }
-    return $this->itemDefinition;
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ListInterface::onChange().
-   */
-  public function onChange($delta) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
+    return $this->getElementDefinition(0);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function get($key) {
-    $elements = $this->getElements();
-    return $elements[$key];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function first() {
-    return $this->get(0);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function set($index, $item) {
-    $this->offsetSet($index, $item);
-    return $this;
+  public function getElementDefinition($key) {
+    $value = isset($this->value[$key]) ? $this->value[$key] : NULL;
+    $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array();
+    return $this->buildDataDefinition($definition, $value, $key);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Config/Schema/Undefined.php b/core/lib/Drupal/Core/Config/Schema/Undefined.php
index 1bcf744..4906de7 100644
--- a/core/lib/Drupal/Core/Config/Schema/Undefined.php
+++ b/core/lib/Drupal/Core/Config/Schema/Undefined.php
@@ -7,15 +7,11 @@
 
 namespace Drupal\Core\Config\Schema;
 
+use Drupal\Core\TypedData\TypedData;
+
 /**
  * Undefined configuration element.
  */
-class Undefined extends Element {
+class Undefined extends TypedData {
 
-  /**
-   * {@inheritdoc}.
-   */
-  public function validate() {
-    return isset($this->value);
-  }
 }
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php
index d775cc2..cd23c77 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManager.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php
@@ -64,8 +64,8 @@ public function __construct(StorageInterface $configStorage, StorageInterface $s
    * @param string $name
    *   Configuration object name.
    *
-   * @return \Drupal\Core\Config\Schema\Element
-   *   Typed configuration element.
+   * @return \Drupal\Core\TypedData\TraversableTypedDataInterface
+   *   Typed configuration data.
    */
   public function get($name) {
     $data = $this->configStorage->read($name);
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
index 84d347b..f9e0f84 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 
 /**
  * Defines an interface for typed configuration manager.
@@ -42,10 +43,9 @@ public function get($name);
    *   - 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.
+   *     parent typed data object implementing TraversableTypedDataInterface.
    *
-   * @return \Drupal\Core\Config\Schema\Element
+   * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The instantiated typed configuration object.
    */
   public function createInstance($data_type, array $configuration = array());
@@ -61,15 +61,14 @@ public function createInstance($data_type, array $configuration = array());
    * @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
+   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $parent
    *   (optional) If a property or list item is to be created, the parent typed
-   *   data object implementing either the ListInterface or the
-   *   ComplexDataInterface.
+   *   data object.
    *
-   * @return \Drupal\Core\Config\Schema\Element
+   * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The instantiated typed data object.
    */
-  public function create(DataDefinitionInterface $definition, $value, $name = NULL, $parent = NULL);
+  public function create(DataDefinitionInterface $definition, $value, $name = NULL, TraversableTypedDataInterface $parent = NULL);
 
   /**
    * Creates a new data definition object from a type definition array and
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 071f584..7ec9afc 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -907,7 +907,7 @@ public function referencedEntities() {
     foreach ($this->getFields() as $field_items) {
       foreach ($field_items as $field_item) {
         // Loop over all properties of a field item.
-        foreach ($field_item->getProperties(TRUE) as $property) {
+        foreach ($field_item->getElements(TRUE) as $property) {
           if ($property instanceof EntityReference && $entity = $property->getValue()) {
             $referenced_entities[] = $entity;
           }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
index bbcb0bb..c482d8b 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
@@ -112,7 +112,7 @@ public function set($property_name, $value, $notify = TRUE) {
   /**
    * {@inheritdoc}
    */
-  public function getProperties($include_computed = FALSE) {
+  public function getElements($include_computed = FALSE) {
     if (!isset($this->entity)) {
       throw new MissingDataException(String::format('Unable to get properties as no entity has been provided.'));
     }
@@ -127,6 +127,13 @@ public function getProperties($include_computed = FALSE) {
   /**
    * {@inheritdoc}
    */
+  public function getElementDefinition($name) {
+    return $this->getDataDefinition()->getPropertyDefinition();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function toArray() {
     if (!isset($this->entity)) {
       throw new MissingDataException(String::format('Unable to get property values as no entity has been provided.'));
@@ -170,7 +177,7 @@ public function getString() {
    */
   public function applyDefaultValue($notify = TRUE) {
     // Apply the default value of all properties.
-    foreach ($this->getProperties() as $property) {
+    foreach ($this->getElements() as $property) {
       $property->applyDefaultValue(FALSE);
     }
     return $this;
diff --git a/core/lib/Drupal/Core/Field/FieldItemListInterface.php b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
index fcaa9b1..9fde3e8 100644
--- a/core/lib/Drupal/Core/Field/FieldItemListInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
@@ -28,7 +28,7 @@
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
  */
-interface FieldItemListInterface extends ListInterface, AccessibleInterface {
+interface FieldItemListInterface extends ListInterface, AccessibleInterface, \Countable, \ArrayAccess {
 
   /**
    * Gets the entity that field belongs to.
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index b60f84d..1a566db 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -24,79 +24,4 @@
  * @ingroup typed_data
  */
 interface ComplexDataInterface extends TraversableTypedDataInterface  {
-
-  /**
-   * Gets a property object.
-   *
-   * @param $property_name
-   *   The name of the property to get; e.g., 'title' or 'name'.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   *   The property object.
-   *
-   * @throws \InvalidArgumentException
-   *   If an invalid property name is given.
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no property can be created.
-   */
-  public function get($property_name);
-
-  /**
-   * Sets a property value.
-   *
-   * @param $property_name
-   *   The name of the property to set; e.g., 'title' or 'name'.
-   * @param $value
-   *   The value to set, or NULL to unset the property.
-   * @param bool $notify
-   *   (optional) Whether to notify the parent object of the change. Defaults to
-   *   TRUE. If the update stems from a parent object, set it to FALSE to avoid
-   *   being notified again.
-   *
-   * @return $this
-   *
-   * @throws \InvalidArgumentException
-   *   If the specified property does not exist.
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no property can be set.
-   */
-  public function set($property_name, $value, $notify = TRUE);
-
-  /**
-   * Gets an array of property objects.
-   *
-   * @param bool $include_computed
-   *   If set to TRUE, computed properties are included. Defaults to FALSE.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface[]
-   *   An array of property objects implementing the TypedDataInterface, keyed
-   *   by property name.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no property can be created.
-   */
-  public function getProperties($include_computed = FALSE);
-
-  /**
-   * Returns an array of all property values.
-   *
-   * Gets an array of plain property values including all not-computed
-   * properties.
-   *
-   * @return array
-   *   An array of property values, keyed by property name.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no property can be created.
-   */
-  public function toArray();
-
-  /**
-   * Determines whether the data structure is empty.
-   *
-   * @return boolean
-   *   TRUE if the data structure is empty, FALSE otherwise.
-   */
-  public function isEmpty();
-
 }
diff --git a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php
index b710a75..f140e53 100644
--- a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php
@@ -101,6 +101,13 @@ public function getItemDefinition() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getElementDefinition($name) {
+    return $this->getItemDefinition();
+  }
+
+  /**
    * Sets the item definition.
    *
    * @param \Drupal\Core\TypedData\DataDefinition $definition
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 09dce07..500467b 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -20,15 +20,7 @@
  *
  * @ingroup typed_data
  */
-interface ListInterface extends TraversableTypedDataInterface, \ArrayAccess, \Countable {
-
-  /**
-   * Determines whether the list contains any non-empty items.
-   *
-   * @return boolean
-   *   TRUE if the list is empty, FALSE otherwise.
-   */
-  public function isEmpty();
+interface ListInterface extends TraversableTypedDataInterface {
 
   /**
    * Gets the definition of a contained item.
@@ -39,37 +31,6 @@ public function isEmpty();
   public function getItemDefinition();
 
   /**
-   * Returns the item at the specified position in this list.
-   *
-   * @param int $index
-   *   Index of the item to return.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   *   The item at the specified position in this list. An empty item is created
-   *   if it does not exist yet.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no item can be created.
-   */
-  public function get($index);
-
-  /**
-   * Replaces the item at the specified position in this list.
-   *
-   * @param int $index
-   *   Index of the item to replace.
-   * @param mixed
-   *   Item to be stored at the specified position.
-   *
-   * @return static
-   *   Returns the list.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no item can be set.
-   */
-  public function set($index, $item);
-
-  /**
    * Returns the first item in this list.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
diff --git a/core/lib/Drupal/Core/TypedData/ListTrait.php b/core/lib/Drupal/Core/TypedData/ListTrait.php
new file mode 100644
index 0000000..aafed5b
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ListTrait.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\TypedData\Plugin\DataType\ListTrait.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Provides a common trait for list implementations.
+ *
+ * @see \Drupal\Core\TypedData\ListInterface
+ */
+trait ListTrait {
+
+  /**
+   * Returns the first item in this list.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The first item in this list.
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If the complex data structure is unset and no item can be created.
+   */
+  public function first() {
+    return $this->get(0);
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
index 7b110c4..24a1c1b 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\Core\TypedData\Plugin\DataType;
 
-use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\ListTrait;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Drupal\Core\TypedData\TypedData;
 use Drupal\Core\TypedData\TypedDataInterface;
 
@@ -28,7 +29,9 @@
  *   definition_class = "\Drupal\Core\TypedData\ListDataDefinition"
  * )
  */
-class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
+class ItemList extends TypedData implements \IteratorAggregate, ListInterface, \Countable, \ArrayAccess {
+
+  use ListTrait;
 
   /**
    * Numerically indexed array items.
@@ -121,7 +124,7 @@ public function get($index) {
   /**
    * {@inheritdoc}
    */
-  public function set($index, $item) {
+  public function set($index, $item, $notify = TRUE) {
     if (is_numeric($index)) {
       // Support setting values via typed data objects.
       if ($item instanceof TypedDataInterface) {
@@ -136,10 +139,10 @@ public function set($index, $item) {
   }
 
   /**
-   * {@inheritdoc}
+   * {@inheritdoc
    */
-  public function first() {
-    return $this->get(0);
+  public function getElements($include_computed = FALSE) {
+    return $this->list;
   }
 
   /**
@@ -175,13 +178,20 @@ protected function createItem($offset = 0, $value = NULL) {
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
+   * {@inheritdoc}
    */
   public function getItemDefinition() {
     return $this->definition->getItemDefinition();
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getElementDefinition($name) {
+    return $this->getItemDefinition();
+  }
+
+  /**
    * Implements \ArrayAccess::offsetSet().
    */
   public function offsetSet($offset, $value) {
@@ -210,12 +220,12 @@ public function count() {
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\ListInterface::isEmpty().
+   * {@inheritdoc}
    */
   public function isEmpty() {
     if (isset($this->list)) {
       foreach ($this->list as $item) {
-        if ($item instanceof ComplexDataInterface || $item instanceof ListInterface) {
+        if ($item instanceof TraversableTypedDataInterface) {
           if (!$item->isEmpty()) {
             return FALSE;
           }
@@ -257,7 +267,7 @@ public function filter($callback) {
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\ListInterface::onChange().
+   * {@inheritdoc}
    */
   public function onChange($delta) {
     // Notify the parent of changes.
@@ -277,4 +287,13 @@ public function __clone() {
       }
     }
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function toArray() {
+    return iterator_to_array($this->getIterator());
+  }
+
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
index 7694db5..7776873 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
@@ -65,6 +65,14 @@ protected function getPropertyDefinitions() {
   /**
    * {@inheritdoc}
    */
+  public function getElementDefinition($name) {
+    return $this->getDataDefinition()->getPropertyDefinition();
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
   public function getValue() {
     // Update the values and return them.
     foreach ($this->properties as $name => $property) {
@@ -111,7 +119,7 @@ public function setValue($values, $notify = TRUE) {
    */
   public function getString() {
     $strings = array();
-    foreach ($this->getProperties() as $property) {
+    foreach ($this->getElements() as $property) {
       $strings[] = $property->getString();
     }
     // Remove any empty strings resulting from empty items.
@@ -168,7 +176,7 @@ protected function writePropertyValue($property_name, $value) {
   /**
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties().
    */
-  public function getProperties($include_computed = FALSE) {
+  public function getElements($include_computed = FALSE) {
     $properties = array();
     foreach ($this->definition->getPropertyDefinitions() as $name => $definition) {
       if ($include_computed || !$definition->isComputed()) {
@@ -181,9 +189,17 @@ public function getProperties($include_computed = FALSE) {
   /**
    * {@inheritdoc}
    */
+  public function count() {
+    return count($this->getValue());
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
   public function toArray() {
     $values = array();
-    foreach ($this->getProperties() as $name => $property) {
+    foreach ($this->getElements() as $name => $property) {
       $values[$name] = $property->getValue();
     }
     return $values;
@@ -193,7 +209,7 @@ public function toArray() {
    * Implements \IteratorAggregate::getIterator().
    */
   public function getIterator() {
-    return new \ArrayIterator($this->getProperties());
+    return new \ArrayIterator($this->getElements());
   }
 
   /**
@@ -246,7 +262,7 @@ public function onChange($property_name, $notify = TRUE) {
    */
   public function applyDefaultValue($notify = TRUE) {
     // Apply the default value of all properties.
-    foreach ($this->getProperties() as $property) {
+    foreach ($this->getElements() as $property) {
       $property->applyDefaultValue(FALSE);
     }
     return $this;
diff --git a/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php
index 34b1df5..a37f15a 100644
--- a/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php
@@ -13,6 +13,81 @@
 interface TraversableTypedDataInterface extends TypedDataInterface, \Traversable {
 
   /**
+   * Gets an element.
+   *
+   * This returns a property object for complex data objects and an item for
+   * item list objects.
+   *
+   * @param string|int $key
+   *   The name of the property or the index of the item to get.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The property object.
+   *
+   * @throws \InvalidArgumentException
+   *   If an invalid property name or item index is given.
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If the data structure is unset and no property can be created.
+   */
+  public function get($key);
+
+  /**
+   * Sets an element value.
+   *
+   * @param $key
+   *   The name of the property or the index of the item to set.
+   * @param $value
+   *   The value to set, or NULL to unset the property.
+   * @param bool $notify
+   *   (optional) Whether to notify the parent object of the change. Defaults to
+   *   TRUE. If the update stems from a parent object, set it to FALSE to avoid
+   *   being notified again.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The property object.
+   *
+   * @throws \InvalidArgumentException
+   *   If the specified property does not exist.
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If the complex data structure is unset and no property can be set.
+   */
+  public function set($key, $value, $notify = TRUE);
+
+  /**
+   * Gets an array of contained elements.
+   *
+   * @param bool $include_computed
+   *   (optional) If set to TRUE, computed properties are included. Defaults to
+   *   FALSE.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface[]
+   *   An array of the contained objects.
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If the complex data structure is unset and no property can be created.
+   */
+  public function getElements($include_computed = FALSE);
+
+  /**
+   * Gets the definition of a contained element.
+   *
+   * @param string|int $key
+   *   The name of the property or the index of the item to get.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface|null
+   *   The definition of the property or NULL if the property does not exist.
+   */
+  public function getElementDefinition($name);
+
+  /**
+   * Determines whether the data object contains any non-empty items.
+   *
+   * @return boolean
+   *   TRUE if the data object is empty, FALSE otherwise.
+   */
+  public function isEmpty();
+
+  /**
    * React to changes to a child property or item.
    *
    * Note that this is invoked after any changes have been applied.
@@ -22,4 +97,18 @@
    */
   public function onChange($name);
 
+  /**
+   * Returns an array of all property values.
+   *
+   * Gets an array of plain property values including all not-computed
+   * properties.
+   *
+   * @return array
+   *   An array of property values, keyed by property name.
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If the data structure is unset and no property can be created.
+   */
+  public function toArray();
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 3216d21..bd7caa7 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -44,6 +44,13 @@
   protected $parent;
 
   /**
+   * The configuration value.
+   *
+   * @var mixed
+   */
+  protected $value;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance($definition, $name = NULL, TraversableTypedDataInterface $parent = NULL) {
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index d42a3d2..38c1264 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -74,8 +74,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
    *   - 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.
+   *     parent typed data object implementing TraversableTypedDataInterface.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The instantiated typed data object.
@@ -110,10 +109,9 @@ public function createInstance($data_type, array $configuration = array()) {
    * @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
+   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $parent
    *   (optional) If a property or list item is to be created, the parent typed
-   *   data object implementing either the ListInterface or the
-   *   ComplexDataInterface.
+   *   data object.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The instantiated typed data object.
@@ -129,7 +127,7 @@ public function createInstance($data_type, array $configuration = array()) {
    * @see \Drupal\Core\TypedData\Plugin\DataType\Uri
    * @see \Drupal\Core\TypedData\Plugin\DataType\Binary
    */
-  public function create(DataDefinitionInterface $definition, $value = NULL, $name = NULL, $parent = NULL) {
+  public function create(DataDefinitionInterface $definition, $value = NULL, $name = NULL, TraversableTypedDataInterface $parent = NULL) {
     $typed_data = $this->createInstance($definition->getDataType(), array(
       'data_definition' => $definition,
       'name' => $name,
@@ -196,9 +194,8 @@ public function createListDataDefinition($item_type) {
    *
    * @param array $options
    *   An array of options with the following keys:
-   *   - object: The parent typed data object, implementing the
-   *     TypedDataInterface and either the ListInterface or the
-   *     ComplexDataInterface.
+   *   - object: The parent typed data object, implementing
+   *     TraversableTypedDataInterface.
    *   - property: The name of the property to instantiate, or the delta of the
    *     the list item to instantiate.
    *   - value: The value to set. If set, it has to match one of the supported
@@ -206,7 +203,7 @@ public function createListDataDefinition($item_type) {
    *
    * @throws \InvalidArgumentException
    *   If the given property is not known, or the passed object does not
-   *   implement the ListInterface or the ComplexDataInterface.
+   *   implement TraversableTypedDataInterface.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The new property instance.
@@ -228,9 +225,8 @@ public function getInstance(array $options) {
    * property path, i.e. all property instances having the same property path
    * and inheriting from the same data type are prototyped.
    *
-   * @param \Drupal\Core\TypedData\TypedDataInterface $object
-   *   The parent typed data object, implementing the TypedDataInterface and
-   *   either the ListInterface or the ComplexDataInterface.
+   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $object
+   *   The parent typed data object.
    * @param string $property_name
    *   The name of the property to instantiate, or the delta of an list item.
    * @param mixed $value
@@ -239,14 +235,14 @@ public function getInstance(array $options) {
    *
    * @throws \InvalidArgumentException
    *   If the given property is not known, or the passed object does not
-   *   implement the ListInterface or the ComplexDataInterface.
+   *   implement TraversableTypedDataInterface.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    *   The new property instance.
    *
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
    */
-  public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) {
+  public function getPropertyInstance(TraversableTypedDataInterface $object, $property_name, $value = NULL) {
     // For performance, try to reuse existing prototypes instead of
     // constructing new objects when possible. A prototype is reused when
     // creating a data object:
@@ -271,17 +267,16 @@ public function getPropertyInstance(TypedDataInterface $object, $property_name,
     $parts[] = $object->getPropertyPath() . '.' . (is_numeric($property_name) ? 0 : $property_name);
     $key = implode(':', $parts);
 
-    // Create the prototype if needed.
+    // Make sure we have a prototype. Then, clone the prototype and set object
+    // specific values, i.e. the value and the context.
     if (!isset($this->prototypes[$key])) {
-      // Fetch the data definition for the child object from the parent.
-      if ($object instanceof ComplexDataInterface) {
-        $definition = $object->getDataDefinition()->getPropertyDefinition($property_name);
-      }
-      elseif ($object instanceof ListInterface) {
-        $definition = $object->getItemDefinition();
+      // Create the initial prototype. For that we need to fetch the definition
+      // of the to be created property instance from the parent.
+      if ($object instanceof TraversableTypedDataInterface) {
+        $definition = $object->getElementDefinition($property_name);
       }
       else {
-        throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface.");
+        throw new \InvalidArgumentException("The passed object has to either implement TraversableTypedDataInterface.");
       }
       if (!$definition) {
         throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.');
diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
index 2858daf..53253ff 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
@@ -7,8 +7,7 @@
 
 namespace Drupal\Core\TypedData\Validation;
 
-use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Symfony\Component\Validator\MetadataFactoryInterface;
 
@@ -30,7 +29,7 @@ public function getMetadataFor($typed_data, $name = '') {
     if (!$typed_data instanceof TypedDataInterface) {
       throw new \InvalidArgumentException('The passed value must be a typed data object.');
     }
-    $is_container = $typed_data instanceof ComplexDataInterface || $typed_data instanceof ListInterface;
+    $is_container = $typed_data instanceof TraversableTypedDataInterface;
     $class = '\Drupal\Core\TypedData\Validation\\' . ($is_container ? 'PropertyContainerMetadata' : 'Metadata');
     return new $class($typed_data, $name, $this);
   }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
index f5850eb..86c06f8 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
@@ -7,8 +7,7 @@
 
 namespace Drupal\Core\TypedData\Validation;
 
-use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Symfony\Component\Validator\PropertyMetadataContainerInterface;
 use Symfony\Component\Validator\ValidationVisitorInterface;
 
@@ -55,10 +54,7 @@ public function hasPropertyMetadata($property_name) {
    * Implements PropertyMetadataContainerInterface::getPropertyMetadata().
    */
   public function getPropertyMetadata($property_name) {
-    if ($this->typedData instanceof ListInterface) {
-      return array(new Metadata($this->typedData[$property_name], $property_name));
-    }
-    elseif ($this->typedData instanceof ComplexDataInterface) {
+    if ($this->typedData instanceof TraversableTypedDataInterface) {
       return array(new Metadata($this->typedData->get($property_name), $property_name));
     }
     else {
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
index 31ffc5b..0913749 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
 
 use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
 use Symfony\Component\Validator\Exception\UnexpectedTypeException;
@@ -33,8 +34,7 @@ public function validate($value, Constraint $constraint) {
 
     foreach ($constraint->properties as $name => $constraints) {
       $property = $value->get($name);
-      $is_container = $property instanceof ComplexDataInterface || $property instanceof ListInterface;
-      if (!$is_container) {
+      if (!($property instanceof TraversableTypedDataInterface)) {
         $property = $property->getValue();
       }
       elseif ($property->isEmpty()) {
diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php
index 202b552..f21501a 100644
--- a/core/modules/config/src/Tests/ConfigSchemaTest.php
+++ b/core/modules/config/src/Tests/ConfigSchemaTest.php
@@ -65,21 +65,21 @@ function testSchemaMapping() {
 
     // Check type detection on elements with undefined types.
     $config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
-    $definition = $config['testitem']->getDataDefinition()->toArray();
+    $definition = $config->get('testitem')->getDataDefinition()->toArray();
     $expected = array();
     $expected['label'] = 'Test item';
     $expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
     $expected['type'] = 'undefined';
     $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
     $this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.');
-    $definition = $config['testlist']->getDataDefinition()->toArray();
+    $definition = $config->get('testlist')->getDataDefinition()->toArray();
     $expected = array();
     $expected['label'] = 'Test list';
     $expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
     $expected['type'] = 'undefined';
     $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
     $this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.');
-    $definition = $config['testnoschema']->getDataDefinition()->toArray();
+    $definition = $config->get('testnoschema')->getDataDefinition()->toArray();
     $expected = array();
     $expected['label'] = 'Undefined';
     $expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
@@ -193,7 +193,7 @@ function testSchemaMapping() {
 
     // Most complex case, get metadata for actual configuration element.
     $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
-    $definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDataDefinition()->toArray();
+    $definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray();
     // This should be the schema for image.effect.image_scale, reuse previous one.
     $expected['type'] =  'image.effect.image_scale';
 
@@ -227,7 +227,7 @@ function testSchemaMappingWithParents() {
 
     // Test fetching parent one level up.
     $entry = $config_data->get('one_level');
-    $definition = $entry['testitem']->getDataDefinition()->toArray();
+    $definition = $entry->get('testitem')->getDataDefinition()->toArray();
     $expected = array(
       'type' => 'config_schema_test.someschema.with_parents.key_1',
       'label' => 'Test item nested one level',
@@ -238,7 +238,7 @@ function testSchemaMappingWithParents() {
 
     // Test fetching parent two levels up.
     $entry = $config_data->get('two_levels');
-    $definition = $entry['wrapper']['testitem']->getDataDefinition()->toArray();
+    $definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
     $expected = array(
       'type' => 'config_schema_test.someschema.with_parents.key_2',
       'label' => 'Test item nested two levels',
@@ -249,7 +249,7 @@ function testSchemaMappingWithParents() {
 
     // Test fetching parent three levels up.
     $entry = $config_data->get('three_levels');
-    $definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition()->toArray();
+    $definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray();
     $expected = array(
       'type' => 'config_schema_test.someschema.with_parents.key_3',
       'label' => 'Test item nested three levels',
@@ -278,28 +278,26 @@ function testSchemaData() {
     $this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
 
     // Check nested array of properties.
-    $list = $meta->get('page');
+    $list = $meta->get('page')->getElements();
     $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
     $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
     $this->assertEqual($list['front']->getValue(), 'user/login', 'Got the right value for page.front data from the list.');
 
-    // And test some ComplexDataInterface methods.
-    $properties = $list->getProperties();
+    // And test some TraversableTypedDataInterface methods.
+    $properties = $list;
     $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
-    $values = $list->toArray();
-    $this->assertTrue(count($values) == 3 && $values['front'] == 'user/login', 'Got the right property values for site page.');
+    $values = $meta->get('page');
+    $this->assertTrue(count($values) == 3 && $values->get('front') == 'user/login', 'Got the right property values for site page.');
 
     // Now let's try something more complex, with nested objects.
     $wrapper = \Drupal::service('config.typed')->get('image.style.large');
     $effects = $wrapper->get('effects');
-
-    // The function is_array() doesn't work with ArrayAccess, so we use count().
     $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data');
     $uuid = key($effects->getValue());
-    $effect = $effects[$uuid];
-    $this->assertTrue(count($effect['data']) && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
-    $this->assertTrue($effect['data']['width'] instanceof IntegerInterface, 'Got the right type for the scale effect width.');
-    $this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' );
+    $effect = $effects->get($uuid)->getElements();
+    $this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
+    $this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
+    $this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' );
 
     // Finally update some object using a configuration wrapper.
     $new_slogan = 'Site slogan for testing configuration metadata';
@@ -406,7 +404,7 @@ function testSchemaFallback() {
    * @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
    */
   function testColonsInSchemaTypeDetermination() {
-    $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests');
+    $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements();
     $definition = $tests[0]->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test.plugin_types.boolean');
 
@@ -419,17 +417,17 @@ function testColonsInSchemaTypeDetermination() {
     $definition = $tests[3]->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test.plugin_types.*');
 
-    $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents');
-    $definition = $tests[0]['settings']->getDataDefinition()->toArray();
+    $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements();
+    $definition = $tests[0]->get('settings')->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean');
 
-    $definition = $tests[1]['settings']->getDataDefinition()->toArray();
+    $definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
 
-    $definition = $tests[2]['settings']->getDataDefinition()->toArray();
+    $definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
 
-    $definition = $tests[3]['settings']->getDataDefinition()->toArray();
+    $definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
   }
 
diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php
index ee0b470..3332cbe 100644
--- a/core/modules/locale/src/LocaleTypedConfig.php
+++ b/core/modules/locale/src/LocaleTypedConfig.php
@@ -9,15 +9,15 @@
 
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
-use Drupal\Core\Config\Schema\Element;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
  * Defines the locale configuration wrapper object.
  */
-class LocaleTypedConfig extends Element {
+class LocaleTypedConfig extends TypedData {
 
   /**
    * The typed configuration data.
diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
index 3d26d37..b9f32f2 100644
--- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
@@ -83,7 +83,7 @@ public function testConfigTranslation() {
 
     // Get translation and check we've only got the site name.
     $translation = $wrapper->getTranslation($langcode);
-    $properties = $translation->getProperties();
+    $properties = $translation->getElements();
     $this->assertEqual(count($properties), 1, 'Got the right number of properties after translation');
 
     // Check the translated site name is displayed.
diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
index 45709b9..ffcbf58 100644
--- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\node\Entity\NodeType;
@@ -566,12 +567,7 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &
 
     // Recurse until a certain depth is reached if possible.
     if ($depth < 7) {
-      if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) {
-        foreach ($wrapper as $item) {
-          $this->getContainedStrings($item, $depth + 1, $strings);
-        }
-      }
-      elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) {
+      if ($wrapper instanceof TraversableTypedDataInterface) {
         foreach ($wrapper as $property) {
           $this->getContainedStrings($property, $depth + 1, $strings);
         }
diff --git a/core/modules/system/src/Tests/TypedData/TypedDataTest.php b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
index 99032f4..8ae1d99 100644
--- a/core/modules/system/src/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
@@ -476,14 +476,14 @@ public function testTypedDataMaps() {
       'three' => 'drei'
     ));
 
-    $properties = $typed_data->getProperties();
+    $properties = $typed_data->getElements();
     $this->assertEqual(array_keys($properties), array_keys($value));
     $this->assertIdentical($properties['one'], $typed_data->get('one'), 'Properties are identical.');
 
     // Test setting a not defined property. It shouldn't show up in the
     // properties, but be kept in the values.
     $typed_data->setValue(array('foo' => 'bar'));
-    $this->assertEqual(array_keys($typed_data->getProperties()), array('one', 'two', 'three'));
+    $this->assertEqual(array_keys($typed_data->getElements()), array('one', 'two', 'three'));
     $this->assertEqual(array_keys($typed_data->getValue()), array('foo', 'one', 'two', 'three'));
 
     // Test getting the string representation.
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index cfa5cd9..b56fcb2 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -957,7 +957,7 @@ public function testCreate() {
       ->will($this->returnValue(array('id' => 'id')));
 
     // ContentEntityStorageBase iterates over the entity which calls this method
-    // internally in ContentEntityBase::getProperties().
+    // internally in ContentEntityBase::getElements().
     $this->entityManager->expects($this->once())
       ->method('getFieldDefinitions')
       ->will($this->returnValue(array()));
diff --git a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php
index 0628c89..2d5cbb5 100644
--- a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php
@@ -322,10 +322,10 @@ public function testSetWithoutData() {
   }
 
   /**
- * @covers ::getProperties
+ * @covers ::getElements
  */
-  public function testGetProperties() {
-    $fields = $this->entityAdapter->getProperties();
+  public function testGetElements() {
+    $fields = $this->entityAdapter->getElements();
     $this->assertInstanceOf('Drupal\Core\Field\FieldItemListInterface', $fields['id']);
     $this->assertInstanceOf('Drupal\Core\Field\FieldItemListInterface', $fields['revision_id']);
   }
