diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
index 8becdd7..479c8cf 100644
--- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
+++ b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
@@ -6,12 +6,14 @@
  */
 
 namespace Drupal\Core\Config\Schema;
+
 use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * 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, TypedConfigInterface {
 
   /**
    * Parsed elements.
@@ -19,19 +21,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 +36,101 @@ protected function getAllKeys() {
    * @return \Drupal\Core\TypedData\TypedDataInterface[]
    *   An array of elements contained in this element.
    */
-  protected abstract function parse();
-
-  /**
-   * Implements TypedDataInterface::validate().
-   */
-  public function validate() {
-    foreach ($this->getElements() as $element) {
-      if (!$element->validate()) {
-        return FALSE;
-      }
+  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 TRUE;
+    return $elements;
   }
 
   /**
-   * Implements ArrayAccess::offsetExists().
+   * Gets data definition object for contained element.
+   *
+   * @param int|string $key
+   *   Property name or index of the element.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
    */
-  public function offsetExists($offset) {
-    return array_key_exists($offset, $this->getElements());
-  }
+  protected abstract function getElementDefinition($key);
 
   /**
-   * Implements ArrayAccess::offsetGet().
+   * {@inheritdoc}
    */
-  public function offsetGet($offset) {
+  public function get($name) {
+    $parts = explode('.', $name);
+    $root_key = array_shift($parts);
     $elements = $this->getElements();
-    return $elements[$offset];
+    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 TypedConfigInterface) {
+          $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 ArrayAccess::offsetSet().
+   * {@inheritdoc}
    */
-  public function offsetSet($offset, $value) {
+  public function set($key, $value) {
+    // Support setting values via typed data objects.
     if ($value instanceof TypedDataInterface) {
       $value = $value->getValue();
     }
-    $this->value[$offset] = $value;
+    $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::offsetUnset().
+   * {@inheritdoc}
    */
-  public function offsetUnset($offset) {
-    unset($this->value[$offset]);
-    unset($this->elements);
+  public function getElements() {
+    if (!isset($this->elements)) {
+      $this->elements = $this->parse();
+    }
+    return $this->elements;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    return empty($this->value);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  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 +140,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
index c492d42..2993f8c 100644
--- a/core/lib/Drupal/Core/Config/Schema/Element.php
+++ b/core/lib/Drupal/Core/Config/Schema/Element.php
@@ -21,20 +21,4 @@
    */
   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..d005302 100644
--- a/core/lib/Drupal/Core/Config/Schema/Ignore.php
+++ b/core/lib/Drupal/Core/Config/Schema/Ignore.php
@@ -12,10 +12,4 @@
  */
 class Ignore extends Element {
 
-  /**
-   * {@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 a85fac4..5adfc20 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -13,145 +13,25 @@
 /**
  * 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;
-  }
+class Mapping extends ArrayElement {
 
   /**
    * {@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 $property;
-  }
-
-  /**
-   * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties().
-   */
-  public function getProperties($include_computed = FALSE) {
-    return $this->getElements();
-  }
-
-  /**
-   * {@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);
-    }
+  protected 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 630bb50..b7c34c2 100644
--- a/core/lib/Drupal/Core/Config/Schema/Sequence.php
+++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php
@@ -7,86 +7,24 @@
 
 namespace Drupal\Core\Config\Schema;
 
-use Drupal\Core\TypedData\ListInterface;
-
 /**
  * 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;
+class Sequence extends ArrayElement {
 
   /**
    * {@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.
+  protected function getElementDefinition($key) {
+    $value = isset($this->value[$key]) ? $this->value[$key] : NULL;
     $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);
-    }
-  }
-
-  /**
-   * {@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;
+    return $this->buildDataDefinition($definition, $value, $key);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php
new file mode 100644
index 0000000..8ce63df
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\TypedConfigInterface.
+ */
+
+namespace Drupal\Core\Config\Schema;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
+
+/**
+ * Interface for a typed configuration object that contains multiple elements.
+ *
+ * A list of typed configuration contains any number of items whose type
+ * will depend on the configuration schema but also on the configuration
+ * data being parsed.
+ *
+ * When implementing this interface which extends Traversable, make sure to list
+ * IteratorAggregate or Iterator before this interface in the implements clause.
+ */
+interface TypedConfigInterface extends TraversableTypedDataInterface {
+
+  /**
+   * Determines whether the data structure is empty.
+   *
+   * @return boolean
+   *   TRUE if the data structure is empty, FALSE otherwise.
+   */
+  public function isEmpty();
+
+  /**
+   * Gets an array of contained elements.
+   *
+   * @return array
+   *   Array of \Drupal\Core\TypedData\TypedDataInterface objects.
+   */
+  public function getElements();
+
+  /**
+   * Gets a contained typed configuration element.
+   *
+   * @param $name
+   *   The name of the property to get; e.g., 'title' or 'name'. Nested
+   *   elements can be get using multiple dot delimited names, for example,
+   *   'page.front'.
+   *
+   * @throws \InvalidArgumentException
+   *   If an invalid property name is given.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The property object.
+   */
+  public function get($name);
+
+  /**
+   * Replaces the item at the specified position in this list.
+   *
+   * @param int|string $key
+   *   Property name or index of the item to replace.
+   * @param mixed $value
+   *   Value to be stored at the specified position. It can be a raw
+   *   configuration value or \Drupal\Core\TypedData\TypedDataInterface
+   *
+   * @return $this
+   */
+  public function set($key, $value);
+
+  /**
+   * Returns an array of all property values.
+   *
+   * @return array
+   *   An array of property values, keyed by property name.
+   */
+  public function toArray();
+
+}
diff --git a/core/lib/Drupal/Core/Config/Schema/Undefined.php b/core/lib/Drupal/Core/Config/Schema/Undefined.php
index 1bcf744..6f62b0d 100644
--- a/core/lib/Drupal/Core/Config/Schema/Undefined.php
+++ b/core/lib/Drupal/Core/Config/Schema/Undefined.php
@@ -12,10 +12,4 @@
  */
 class Undefined extends Element {
 
-  /**
-   * {@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..439ea8d 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\Config\TypedConfigInterface
+   *   Typed configuration data.
    */
   public function get($name) {
     $data = $this->configStorage->read($name);
diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php
index 7005002..e8e04f4 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 TypedConfigInterface methods.
+    $properties = $list;
     $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
-    $values = $list->toArray();
+    $values = $meta->get('page')->toArray();
     $this->assertTrue(count($values) == 3 && $values['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');
+    $this->assertTrue(count($effects->toArray()) == 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/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
index 5e05293..c082ccc 100644
--- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
@@ -16,6 +16,8 @@
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\language\Config\LanguageConfigOverride;
 use Drupal\language\ConfigurableLanguageManagerInterface;
 use Drupal\locale\StringStorageInterface;
@@ -240,7 +242,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   /**
    * Formats configuration schema as a form tree.
    *
-   * @param \Drupal\Core\Config\Schema\Element $schema
+   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $schema
    *   Schema definition of configuration.
    * @param array|string $config_data
    *   Configuration object of requested language, a string when done traversing
@@ -257,7 +259,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * @return array
    *   An associative array containing the structure of the form.
    */
-  protected function buildConfigForm(Element $schema, $config_data, $base_config_data, $open = TRUE, $base_key = '') {
+  protected function buildConfigForm(TraversableTypedDataInterface $schema, $config_data, $base_config_data, $open = TRUE, $base_key = '') {
     $build = array();
     foreach ($schema as $key => $element) {
       // Make the specific element key, "$base_key.$key".
@@ -266,7 +268,7 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
       if (!$definition->getLabel()) {
         $definition->setLabel($this->t('N/A'));
       }
-      if ($element instanceof Element) {
+      if ($element instanceof TraversableTypedDataInterface) {
         // Build sub-structure and include it with a wrapper in the form
         // if there are any translatable elements there.
         $sub_build = $this->buildConfigForm($element, $config_data[$key], $base_config_data[$key], FALSE, $element_key);
diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php
index ee0b470..2c225d3 100644
--- a/core/modules/locale/src/LocaleTypedConfig.php
+++ b/core/modules/locale/src/LocaleTypedConfig.php
@@ -10,6 +10,7 @@
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\Config\Schema\Element;
+use Drupal\Core\Config\Schema\TypedConfigInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\TypedData\TraversableTypedDataInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
index e0c20fc..1663784 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.
