diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
deleted file mode 100644
index f801c28..0000000
--- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Config\Schema\ArrayElement.
- */
-
-namespace Drupal\Core\Config\Schema;
-
-use \ArrayAccess;
-use \ArrayIterator;
-use \Countable;
-use \IteratorAggregate;
-use \Traversable;
-
-/**
- * Defines a generic configuration element that contains multiple properties.
- */
-abstract class ArrayElement extends Element implements IteratorAggregate, ArrayAccess, Countable {
-
-  /**
-   * Parsed elements.
-   */
-  protected $elements;
-
-  /**
-   * Gets an array of contained elements.
-   *
-   * @return array
-   *   Array of \Drupal\Core\Config\Schema\ArrayElement objects.
-   */
-  protected function getElements() {
-    if (!isset($this->elements)) {
-      $this->elements = $this->parse();
-    }
-    return $this->elements;
-  }
-
-  /**
-   * Gets valid configuration data keys.
-   *
-   * @return array
-   *   Array of valid configuration data keys.
-   */
-  protected function getAllKeys() {
-    return is_array($this->value) ? array_keys($this->value) : array();
-  }
-
-  /**
-   * Builds an array of contained elements.
-   *
-   * @return array
-   *   Array of \Drupal\Core\Config\Schema\ArrayElement objects.
-   */
-  protected abstract function parse();
-
-  /**
-   * Implements TypedDataInterface::validate().
-   */
-  public function validate() {
-    foreach ($this->getElements() as $element) {
-      if (!$element->validate()) {
-        return FALSE;
-      }
-    }
-    return TRUE;
-  }
-
-  /**
-   * Implements ArrayAccess::offsetExists().
-   */
-  public function offsetExists($offset) {
-    return array_key_exists($offset, $this->getElements());
-  }
-
-  /**
-   * Implements ArrayAccess::offsetGet().
-   */
-  public function offsetGet($offset) {
-    $elements = $this->getElements();
-    return $elements[$offset];
-  }
-
-  /**
-   * Implements ArrayAccess::offsetSet().
-   */
-  public function offsetSet($offset, $value) {
-    if ($value instanceof TypedDataInterface) {
-      $value = $value->getValue();
-    }
-    $this->value[$offset] = $value;
-    unset($this->elements);
-  }
-
-  /**
-   * Implements ArrayAccess::offsetUnset().
-   */
-  public function offsetUnset($offset) {
-    unset($this->value[$offset]);
-    unset($this->elements);
-  }
-
-  /**
-   * Implements Countable::count().
-   */
-  public function count() {
-    return count($this->getElements());
-  }
-
-  /**
-   * Implements IteratorAggregate::getIterator();
-   */
-  public function getIterator() {
-    return new ArrayIterator($this->getElements());
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php
index c492d42..f8bd27f 100644
--- a/core/lib/Drupal/Core/Config/Schema/Element.php
+++ b/core/lib/Drupal/Core/Config/Schema/Element.php
@@ -22,14 +22,33 @@
   protected $value;
 
   /**
-   * Create typed config object.
+   * 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 parseElement($key, $data, $definition) {
-    return \Drupal::service('config.typed')->create($definition, $data, $key, $this);
+  protected function createElement($definition, $value, $key) {
+    return \Drupal::service('config.typed')->create($definition, $value, $key, $this);
   }
 
   /**
-   * Build data definition object for contained elements.
+   * 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
    */
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..99dc930 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 TypedConfigListBase {
 
   /**
    * {@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);
-    }
+  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/SchemaCheckTrait.php b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
deleted file mode 100644
index 536a4cb..0000000
--- a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Config\Schema\SchemaCheckTrait.
- */
-
-namespace Drupal\Core\Config\Schema;
-
-use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\TypedData\PrimitiveInterface;
-use Drupal\Core\TypedData\Type\BooleanInterface;
-use Drupal\Core\TypedData\Type\StringInterface;
-use Drupal\Core\TypedData\Type\FloatInterface;
-use Drupal\Core\TypedData\Type\IntegerInterface;
-
-/**
- * Provides a trait for checking configuration schema.
- */
-trait SchemaCheckTrait {
-
-  /**
-   * The config schema wrapper object for the configuration object under test.
-   *
-   * @var \Drupal\Core\Config\Schema\Element
-   */
-  protected $schema;
-
-  /**
-   * The configuration object name under test.
-   *
-   * @var string
-   */
-  protected $configName;
-
-  /**
-   * Checks the TypedConfigManager has a valid schema for the configuration.
-   *
-   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
-   *   The TypedConfigManager.
-   * @param string $config_name
-   *   The configuration name.
-   * @param array $config_data
-   *   The configuration data.
-   *
-   * @return array|bool
-   *   FALSE if no schema found. List of errors if any found. TRUE if fully
-   *   valid.
-   */
-  public function checkConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) {
-    $this->configName = $config_name;
-    if (!$typed_config->hasConfigSchema($config_name)) {
-      return FALSE;
-    }
-    $definition = $typed_config->getDefinition($config_name);
-    $data_definition = $typed_config->buildDataDefinition($definition, $config_data);
-    $this->schema = $typed_config->create($data_definition, $config_data);
-    $errors = array();
-    foreach ($config_data as $key => $value) {
-      $errors = array_merge($errors, $this->checkValue($key, $value));
-    }
-    if (empty($errors)) {
-      return TRUE;
-    }
-    return $errors;
-  }
-
-  /**
-   * Helper method to check data type.
-   *
-   * @param string $key
-   *   A string of configuration key.
-   * @param mixed $value
-   *   Value of given key.
-   *
-   * @return array
-   *   List of errors found while checking with the corresponding schema.
-   */
-  protected function checkValue($key, $value) {
-    $error_key = $this->configName . ':' . $key;
-    $element = $this->schema->get($key);
-    if ($element instanceof Undefined) {
-      // @todo Temporary workaround for https://www.drupal.org/node/2224761.
-      $key_parts = explode('.', $key);
-      if (array_pop($key_parts) == 'translation_sync' && strpos($this->configName, 'field.') === 0) {
-        return array();
-      }
-      return array($error_key => 'Missing schema.');
-    }
-
-    // Do not check value if it is defined to be ignored.
-    if ($element && $element instanceof Ignore) {
-      return array();
-    }
-
-    if ($element && is_scalar($value) || $value === NULL) {
-      $success = FALSE;
-      $type = gettype($value);
-      if ($element instanceof PrimitiveInterface) {
-        $success =
-          ($type == 'integer' && $element instanceof IntegerInterface) ||
-          ($type == 'double' && $element instanceof FloatInterface) ||
-          ($type == 'boolean' && $element instanceof BooleanInterface) ||
-          ($type == 'string' && $element instanceof StringInterface) ||
-          // Null values are allowed for all types.
-          ($value === NULL);
-      }
-      $class = get_class($element);
-      if (!$success) {
-        return array($error_key => "Variable type is $type but applied schema class is $class.");
-      }
-    }
-    else {
-      $errors = array();
-      if (!$element instanceof ArrayElement) {
-        $errors[$error_key] = 'Non-scalar value but not defined as an array (such as mapping or sequence).';
-      }
-
-      // Go on processing so we can get errors on all levels. Any non-scalar
-      // value must be an array so cast to an array.
-      if (!is_array($value)) {
-        $value = (array) $value;
-      }
-      // Recurse into any nested keys.
-      foreach ($value as $nested_value_key => $nested_value) {
-        $errors = array_merge($errors, $this->checkValue($key . '.' . $nested_value_key, $nested_value));
-      }
-      return $errors;
-    }
-    // No errors found.
-    return array();
-  }
-}
diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php
index e5b6a35..314e0c5 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;
-
-  /**
-   * Overrides ArrayElement::parse()
-   */
-  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);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function get($key) {
-    $elements = $this->getElements();
-    return $elements[$key];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function first() {
-    return $this->get(0);
-  }
+class Sequence extends TypedConfigListBase {
 
   /**
    * {@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/TypedConfig.php b/core/lib/Drupal/Core/Config/Schema/TypedConfig.php
new file mode 100644
index 0000000..41e02b5
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Schema/TypedConfig.php
@@ -0,0 +1,285 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\Schema\TypedConfig.
+ */
+
+namespace Drupal\Core\Config\Schema;
+
+use Drupal\Core\Config\ConfigBase;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\BooleanInterface;
+use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+
+/**
+ * Provides a base class for configuration objects with get/set support.
+ *
+ * Encapsulates all capabilities needed for runtime configuration handling for
+ * a specific configuration object.
+ *
+ * Extend directly from this class for non-storable configuration where the
+ * configuration API is desired but storage is not possible; for example, if
+ * the data is derived at runtime. For storable configuration, extend
+ * \Drupal\Core\Config\StorableConfigBase.
+ *
+ * @see \Drupal\Core\Config\StorableConfigBase
+ * @see \Drupal\Core\Config\Config
+ * @see \Drupal\Core\Theme\ThemeSettings
+ */
+class TypedConfig extends ConfigBase {
+
+  /**
+   * The data definition.
+   *
+   * @var \Drupal\Core\TypedData\DataDefinitionInterface
+   */
+  protected $dataDefinition;
+
+  /**
+   * The typed configuration data.
+   *
+   * @var \Drupal\Core\Config\Schema\Element
+   */
+  protected $typedConfigData;
+
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManagerInterface
+   */
+  protected $typedConfigManager;
+
+  /**
+   * Constructs a configuration object.
+   *
+   * @param string $name
+   *   The name of the configuration object.
+   * @param array $data
+   *   The configuration data.
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
+   *   The typed configuration manager service.
+   */
+  public function __construct($name, $data, TypedConfigManagerInterface $typed_config) {
+    $this->name = $name;
+    $this->data = $data;
+    $this->typedConfigManager = $typed_config;
+  }
+
+  /**
+   * Gets raw configuration data.
+   *
+   * @return array
+   *   Array of configuration data.
+   */
+  public function getData() {
+    return $this->data;
+  }
+
+  /**
+   * Gets wrapped typed config object.
+   *
+   * @return \Drupal\Core\Config\TypedConfigListInterface
+   *   The typed configuration data.
+   */
+  public function getTypedConfigData() {
+    if (!isset($this->typedConfigData)) {
+      $this->typedConfigData = $this->typedConfigManager->create($this->getDataDefinition(), $this->data);
+    }
+    return $this->typedConfigData;
+  }
+
+  /**
+   * Gets the data definition.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   *   The data definition object.
+   */
+  public function getDataDefinition() {
+    if (!isset($this->dataDefinition)) {
+      $type_definition = $this->typedConfigManager->getDefinition($this->name);
+      return $this->typedConfigManager->buildDataDefinition($type_definition, $this->data);
+    }
+    return $this->dataDefinition;
+  }
+
+  /**
+   * Gets a contained typed configuration element.
+   *
+   * @param $key
+   *   The key of the element 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 key is given.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The property object.
+   */
+  public function get($key = '') {
+    $elements = $this->getTypedConfigData();
+    if (empty($key)) {
+      return $elements;
+    }
+    $parts = explode('.', $key);
+    $root_key = array_shift($parts);
+
+    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 TypedConfigListInterface) {
+          $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)));
+    }
+  }
+
+  /**
+   * 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) {
+    // Support setting values via typed data objects.
+    if ($value instanceof TypedDataInterface) {
+      $value = $value->getValue();
+    }
+    parent::set($key, $value);
+    $this->reset();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setData(array $data) {
+    parent::setData($data);
+    $this->reset();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clear($key) {
+    parent::clear($key);
+    $this->reset();
+    return $this;
+  }
+
+  /**
+   * Reset built typed configuration data and data definition.
+   */
+  protected function reset() {
+    unset($this->typedConfigData);
+    unset($this->dataDefinition);
+  }
+
+  /**
+   * Checks the TypedConfigManager has a valid schema for the configuration.
+   *
+   * @return array|bool
+   *   FALSE if no schema found. List of errors if any found. TRUE if fully
+   *   valid.
+   */
+  public function checkConfigSchema() {
+    if (!$this->typedConfigManager->hasConfigSchema($this->name)) {
+      return FALSE;
+    }
+    $errors = array();
+    foreach ($this->getData() as $key => $value) {
+      $errors = array_merge($errors, $this->checkValue($key, $value));
+    }
+    if (empty($errors)) {
+      return TRUE;
+    }
+    return $errors;
+  }
+
+  /**
+   * Helper method to check data type.
+   *
+   * @param string $key
+   *   A string of configuration key.
+   * @param mixed $value
+   *   Value of given key.
+   *
+   * @return array
+   *   List of errors found while checking with the corresponding schema.
+   */
+  protected function checkValue($key, $value) {
+    $error_key = $this->name . ':' . $key;
+    $element = $this->get($key);
+    if ($element instanceof Undefined) {
+      // @todo Temporary workaround for https://www.drupal.org/node/2224761.
+      $key_parts = explode('.', $key);
+      if (array_pop($key_parts) == 'translation_sync' && strpos($this->name, 'field.') === 0) {
+        return array();
+      }
+      return array($error_key => 'Missing schema.');
+    }
+
+    // Do not check value if it is defined to be ignored.
+    if ($element && $element instanceof Ignore) {
+      return array();
+    }
+
+    if ($element && is_scalar($value) || $value === NULL) {
+      $success = FALSE;
+      $type = gettype($value);
+      if ($element instanceof PrimitiveInterface) {
+        $success =
+          ($type == 'integer' && $element instanceof IntegerInterface) ||
+          ($type == 'double' && $element instanceof FloatInterface) ||
+          ($type == 'boolean' && $element instanceof BooleanInterface) ||
+          ($type == 'string' && $element instanceof StringInterface) ||
+          // Null values are allowed for all types.
+          ($value === NULL);
+      }
+      $class = get_class($element);
+      if (!$success) {
+        return array($error_key => "Variable type is $type but applied schema class is $class.");
+      }
+    }
+    else {
+      $errors = array();
+      if (!$element instanceof TypedConfigListInterface) {
+        $errors[$error_key] = 'Non-scalar value but not defined as an array (such as mapping or sequence).';
+      }
+
+      // Go on processing so we can get errors on all levels. Any non-scalar
+      // value must be an array so cast to an array.
+      if (!is_array($value)) {
+        $value = (array) $value;
+      }
+      // Recurse into any nested keys.
+      foreach ($value as $nested_value_key => $nested_value) {
+        $errors = array_merge($errors, $this->checkValue($key . '.' . $nested_value_key, $nested_value));
+      }
+      return $errors;
+    }
+    // No errors found.
+    return array();
+  }
+}
+
diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php
new file mode 100644
index 0000000..bb74726
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\Schema\TypedConfigListBase.
+ */
+
+namespace Drupal\Core\Config\Schema;
+
+use Drupal\Component\Utility\String;
+use \ArrayAccess;
+use \ArrayIterator;
+use \Countable;
+use \IteratorAggregate;
+use \Traversable;
+
+/**
+ * Defines a generic configuration element that contains multiple properties.
+ */
+abstract class TypedConfigListBase extends Element implements IteratorAggregate, TypedConfigListInterface {
+
+  /**
+   * Parsed elements.
+   */
+  protected $elements;
+
+  /**
+   * Gets valid configuration data keys.
+   *
+   * @return array
+   *   Array of valid configuration data keys.
+   */
+  protected function getAllKeys() {
+    return is_array($this->value) ? array_keys($this->value) : array();
+  }
+
+  /**
+   * Builds an array of contained elements.
+   *
+   * @return array
+   *   Array of \Drupal\Core\Config\Schema\TypedConfigListBase objects.
+   */
+  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;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($property_name) {
+    $elements = $this->getElements();
+    if (isset($elements[$property_name])) {
+      return $elements[$property_name];
+    }
+    else {
+      throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name)));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($key, $value) {
+    // 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;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  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();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($key) {
+    // Notify the parent of changes.
+    if (isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * Implements ArrayAccess::offsetExists().
+   */
+  public function offsetExists($offset) {
+    return array_key_exists($offset, $this->getElements());
+  }
+
+  /**
+   * Implements ArrayAccess::offsetGet().
+   */
+  public function offsetGet($offset) {
+    $elements = $this->getElements();
+    return $elements[$offset];
+  }
+
+  /**
+   * Implements ArrayAccess::offsetSet().
+   */
+  public function offsetSet($offset, $value) {
+    $this->set($offset, $value);
+  }
+
+  /**
+   * Implements ArrayAccess::offsetUnset().
+   */
+  public function offsetUnset($offset) {
+    unset($this->value[$offset]);
+    unset($this->elements);
+  }
+
+  /**
+   * Implements Countable::count().
+   */
+  public function count() {
+    return count($this->getElements());
+  }
+
+  /**
+   * Implements IteratorAggregate::getIterator();
+   */
+  public function getIterator() {
+    return new ArrayIterator($this->getElements());
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php
new file mode 100644
index 0000000..0289bfd
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\TypedConfigListInterface.
+ */
+
+namespace Drupal\Core\Config\Schema;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+
+/**
+ * Interface for a list of typed configuration 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 TypedConfigListInterface extends TypedDataInterface, \ArrayAccess, \Countable, \Traversable {
+
+  /**
+   * Determines whether the list contains any non-empty items.
+   *
+   * @return boolean
+   *   TRUE if the list 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 data definition object for contained element.
+   *
+   * @param int|string $key
+   *   Property name or index of the element.
+   *
+   * @return \Drupal\Core\TypedData\DataDefinitionInterface
+   */
+  public function getElementDefinition($key);
+
+  /**
+   * Gets a contained typed configuration element.
+   *
+   * @param $property_name
+   *   The name of the property to get; e.g., 'title' or 'name'.
+   *
+   * @throws \InvalidArgumentException
+   *   If an invalid property name is given.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The property object.
+   */
+  public function get($property_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();
+
+  /**
+   * React to changes to a child element.
+   *
+   * Note that this is invoked after any changes have been applied.
+   *
+   * @param $key
+   *   The key of the property which is changed.
+   */
+  public function onChange($key);
+}
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/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php
index 5c6e8b3..8dfd802 100644
--- a/core/lib/Drupal/Core/Config/StorableConfigBase.php
+++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php
@@ -9,10 +9,11 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Config\Schema\Ignore;
+use Drupal\Core\Config\Schema\TypedConfig;
+use Drupal\Core\Config\Schema\Undefined;
 use Drupal\Core\TypedData\PrimitiveInterface;
 use Drupal\Core\TypedData\Type\FloatInterface;
 use Drupal\Core\TypedData\Type\IntegerInterface;
-use Drupal\Core\Config\Schema\Undefined;
 
 /**
  * Provides a base class for configuration objects with storage support.
@@ -122,13 +123,11 @@ public function getStorage() {
    * data structure, so if the name or the data changes in any way, the wrapper
    * should be reset.
    *
-   * @return \Drupal\Core\Config\Schema\Element
+   * @return \Drupal\Core\Config\Schema\TypedConfig
    */
   protected function getSchemaWrapper() {
     if (!isset($this->schemaWrapper)) {
-      $definition = $this->typedConfigManager->getDefinition($this->name);
-      $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $this->data);
-      $this->schemaWrapper = $this->typedConfigManager->create($data_definition, $this->data);
+      $this->schemaWrapper = new TypedConfig($this->name, $this->data, $this->typedConfigManager);
     }
     return $this->schemaWrapper;
   }
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php
index ba3f353..4a24053 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManager.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\Schema\TypedConfig;
 use Drupal\Core\TypedData\TypedDataManager;
 
 /**
@@ -73,14 +74,12 @@ 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\Schema\TypedConfig
+   *   Typed typed configuration wrapper.
    */
   public function get($name) {
     $data = $this->configStorage->read($name);
-    $type_definition = $this->getDefinition($name);
-    $data_definition =  $this->buildDataDefinition($type_definition, $data);
-    return $this->create($data_definition, $data);
+    return new TypedConfig($name, $data, $this);
   }
 
   /**
diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php
index 7401247..07c74a7 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';
@@ -280,8 +280,8 @@ function testSchemaData() {
     $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', 'Got the right value for page.front data from the list.');
 
-    // And test some ComplexDataInterface methods.
-    $properties = $list->getProperties();
+    // And test some TypedConfigListInterface methods.
+    $properties = $list->getElements();
     $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', 'Got the right property values for site page.');
diff --git a/core/modules/config/src/Tests/SchemaCheckTestTrait.php b/core/modules/config/src/Tests/SchemaCheckTestTrait.php
index c5b4cba..a9f31a6 100644
--- a/core/modules/config/src/Tests/SchemaCheckTestTrait.php
+++ b/core/modules/config/src/Tests/SchemaCheckTestTrait.php
@@ -8,7 +8,7 @@
 namespace Drupal\config\Tests;
 
 use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Config\Schema\SchemaCheckTrait;
+use Drupal\Core\Config\Schema\TypedConfig;
 use Drupal\Component\Utility\String;
 
 /**
@@ -16,8 +16,6 @@
  */
 trait SchemaCheckTestTrait {
 
-  use SchemaCheckTrait;
-
   /**
    * Asserts the TypedConfigManager has a valid schema for the configuration.
    *
@@ -29,7 +27,8 @@
    *   The configuration data.
    */
   public function assertConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) {
-    $errors = $this->checkConfigSchema($typed_config, $config_name, $config_data);
+    $config_wrapper = new TypedConfig($config_name, $config_data, $typed_config);
+    $errors = $config_wrapper->checkConfigSchema();
     if ($errors === FALSE) {
       // @todo Since the use of this trait is under TestBase, it works.
       //  Can be fixed as part of https://drupal.org/node/2260053.
diff --git a/core/modules/config/src/Tests/SchemaCheckTraitTest.php b/core/modules/config/src/Tests/SchemaCheckTraitTest.php
index f7dd11e..28f4e3c 100644
--- a/core/modules/config/src/Tests/SchemaCheckTraitTest.php
+++ b/core/modules/config/src/Tests/SchemaCheckTraitTest.php
@@ -7,17 +7,18 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\Core\Config\Schema\SchemaCheckTrait;
+use Drupal\Core\Config\Schema\TypedConfig;
 use Drupal\simpletest\KernelTestBase;
 
 
 /**
  * Tests the functionality of SchemaCheckTrait.
  *
+ * @todo Rename, that is not a trait anymore
+ *
  * @group config
  */
 class SchemaCheckTraitTest extends KernelTestBase {
-  use SchemaCheckTrait;
 
   /**
    * The typed config manager.
@@ -47,19 +48,22 @@ public function setUp() {
    */
   public function testTrait() {
     // Test a non existing schema.
-    $ret = $this->checkConfigSchema($this->typedConfig, 'config_schema_test.noschema', \Drupal::config('config_schema_test.noschema')->get());
+    $typed_config = new TypedConfig('config_schema_test.noschema', \Drupal::config('config_schema_test.noschema')->get(), $this->typedConfig);
+    $ret = $typed_config->checkConfigSchema();
     $this->assertIdentical($ret, FALSE);
 
     // Test an existing schema with valid data.
     $config_data = \Drupal::config('config_test.types')->get();
-    $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
+    $typed_config = new TypedConfig('config_test.types', $config_data, $this->typedConfig);
+    $ret = $typed_config->checkConfigSchema();
     $this->assertIdentical($ret, TRUE);
 
     // Add a new key, a new array and overwrite boolean with array to test the
     // error messages.
     $config_data = array('new_key' => 'new_value', 'new_array' => array()) + $config_data;
     $config_data['boolean'] = array();
-    $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
+    $typed_config = new TypedConfig('config_test.types', $config_data, $this->typedConfig);
+    $ret = $typed_config->checkConfigSchema();
     $expected = array(
       'config_test.types:new_key' => 'Missing schema.',
       'config_test.types:new_array' => 'Missing schema.',
diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php
index 8eb3735..40aa5cb 100644
--- a/core/modules/config_translation/src/ConfigMapperManager.php
+++ b/core/modules/config_translation/src/ConfigMapperManager.php
@@ -10,7 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Config\Schema\ArrayElement;
+use Drupal\Core\Config\Schema\TypedConfigListInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ThemeHandlerInterface;
@@ -161,7 +161,7 @@ protected function findDefinitions() {
    * {@inheritdoc}
    */
   public function hasTranslatable($name) {
-    return $this->findTranslatable($this->typedConfigManager->get($name));
+    return $this->findTranslatable($this->typedConfigManager->get($name)->get());
   }
 
   /**
@@ -176,7 +176,7 @@ public function hasTranslatable($name) {
   protected function findTranslatable(TypedDataInterface $element) {
     // In case this is a sequence or a mapping check whether any child element
     // is translatable.
-    if ($element instanceof ArrayElement) {
+    if ($element instanceof TypedConfigListInterface) {
       foreach ($element as $child_element) {
         if ($this->findTranslatable($child_element)) {
           return TRUE;
diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
index 5b6125a..83707eb 100644
--- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
@@ -195,7 +195,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
     );
     foreach ($this->mapper->getConfigNames() as $name) {
       $form['config_names'][$name] = array('#type' => 'container');
-      $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name), $config_factory->get($name)->get(), $this->baseConfigData[$name]);
+      $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name)->get(), $config_factory->get($name)->get(), $this->baseConfigData[$name]);
     }
 
     $form['actions']['#type'] = 'actions';
diff --git a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php
index d7c47cb..528d8aa 100644
--- a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php
+++ b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php
@@ -171,7 +171,7 @@ protected function getElement(array $definition) {
    */
   protected function getNestedElement(array $elements) {
     // ConfigMapperManager::findTranslatable() checks for the abstract class
-    // \Drupal\Core\Config\Schema\ArrayElement, but mocking that directly does
+    // \Drupal\Core\Config\Schema\TypedConfigListBase, but mocking that directly does
     // not work.
     $nested_element = $this->getMockBuilder('Drupal\Core\Config\Schema\Mapping')
       ->disableOriginalConstructor()
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 3dd25b7..3abbeb1 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -629,7 +629,7 @@ function locale_config_update_multiple(array $names, $langcodes = array()) {
   foreach ($names as $name) {
     $wrapper = \Drupal\locale\Locale::config()->get($name);
     foreach ($langcodes as $langcode) {
-      $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode)->getValue() : NULL;
+      $translation = $wrapper->getData() ? $wrapper->getTranslation($langcode)->getValue() : NULL;
       if ($translation) {
         \Drupal\locale\Locale::config()->saveTranslationData($name, $langcode, $translation);
         $count++;
diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php
index 73d653c..687d803 100644
--- a/core/modules/locale/src/LocaleConfigManager.php
+++ b/core/modules/locale/src/LocaleConfigManager.php
@@ -106,13 +106,9 @@ public function get($name) {
     $updated = $this->configStorage->read($name);
     // We get only the data that didn't change from default.
     $data = $this->compareConfigData($default, $updated);
-    $definition = $this->typedConfigManager->getDefinition($name);
-    $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $data);
     // Unless the configuration has a explicit language code we assume English.
     $langcode = isset($default['langcode']) ? $default['langcode'] : 'en';
-    $wrapper = new LocaleTypedConfig($data_definition, $name, $langcode, $this, $this->typedConfigManager);
-    $wrapper->setValue($data);
-    return $wrapper;
+    return new LocaleTypedConfig($name, $data, $langcode, $this, $this->typedConfigManager);
   }
 
   /**
diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php
index cbe9d1a..27a65e8 100644
--- a/core/modules/locale/src/LocaleTypedConfig.php
+++ b/core/modules/locale/src/LocaleTypedConfig.php
@@ -10,20 +10,14 @@
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\Config\Schema\Element;
-use Drupal\Core\Config\Schema\ArrayElement;
+use Drupal\Core\Config\Schema\TypedConfig;
+use Drupal\Core\Config\Schema\TypedConfigListInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 
 /**
  * Defines the locale configuration wrapper object.
  */
-class LocaleTypedConfig extends Element {
-
-  /**
-   * The typed configuration data.
-   *
-   * @var \Drupal\Core\Config\Schema\Element
-   */
-  protected $typedConfig;
+class LocaleTypedConfig extends TypedConfig {
 
   /**
    * The language code for which this is a translation.
@@ -40,19 +34,12 @@ class LocaleTypedConfig extends Element {
   protected $localeConfig;
 
   /**
-   * The typed config manager.
-   *
-   * @var \Drupal\Core\Config\TypedConfigManagerInterface
-   */
-  protected $typedConfigManager;
-
-  /**
    * Constructs a configuration wrapper object.
    *
-   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
-   *   The data definition.
    * @param string $name
    *   The configuration object name.
+   * @param array $data
+   *   The configuration data.
    * @param string $langcode
    *   Language code for the source configuration data.
    * @param \Drupal\locale\LocaleConfigManager $locale_config
@@ -60,18 +47,10 @@ class LocaleTypedConfig extends Element {
    * @param \Drupal\locale\TypedConfigManagerInterface $typed_config;
    *   The typed configuration manager interface.
    */
-  public function __construct(DataDefinitionInterface $definition, $name, $langcode, LocaleConfigManager $locale_config, TypedConfigManagerInterface $typed_config) {
-    parent::__construct($definition, $name);
+  public function __construct($name, $data, $langcode, LocaleConfigManager $locale_config, TypedConfigManagerInterface $typed_config) {
+    parent::__construct($name, $data, $typed_config);
     $this->langcode = $langcode;
     $this->localeConfig = $locale_config;
-    $this->typedConfigManager = $typed_config;
-  }
-
-  /**
-   * Gets wrapped typed config object.
-   */
-  public function getTypedConfig() {
-    return $this->typedConfigManager->create($this->definition, $this->value);
   }
 
   /**
@@ -82,8 +61,8 @@ public function getTranslation($langcode) {
       'source' => $this->langcode,
       'target' => $langcode,
     );
-    $data = $this->getElementTranslation($this->getTypedConfig(), $options);
-    return $this->typedConfigManager->create($this->definition, $data);
+    $data = $this->getElementTranslation(clone $this->getTypedConfigData(), $options);
+    return $this->typedConfigManager->create($this->getDataDefinition(), $data);
   }
 
   /**
@@ -116,7 +95,7 @@ protected function canTranslate($from_langcode, $to_langcode) {
    *
    * @param mixed $element
    *   Typed configuration element, either \Drupal\Core\Config\Schema\Element or
-   *   \Drupal\Core\Config\Schema\ArrayElement.
+   *   \Drupal\Core\Config\Schema\TypedConfigListInterface.
    * @param array $options
    *   Array with translation options that must contain the keys defined in
    *   \Drupal\locale\LocaleTypedConfig::translateElement().
@@ -127,7 +106,7 @@ protected function canTranslate($from_langcode, $to_langcode) {
    */
   protected function getElementTranslation($element, array $options) {
     $translation = array();
-    if ($element instanceof ArrayElement) {
+    if ($element instanceof TypedConfigListInterface) {
       $translation = $this->getArrayTranslation($element, $options);
     }
     elseif ($this->translateElement($element, $options)) {
@@ -137,9 +116,9 @@ protected function getElementTranslation($element, array $options) {
   }
 
   /**
-   * Gets translated configuration data for an element of type ArrayElement.
+   * Gets translated configuration data for an element of type TypedConfigListInterface.
    *
-   * @param \Drupal\Core\Config\Schema\ArrayElement $element
+   * @param \Drupal\Core\Config\Schema\TypedConfigListInterface $element
    *   Typed configuration array element.
    * @param array $options
    *   Array with translation options that must contain the keys defined in
@@ -148,7 +127,7 @@ protected function getElementTranslation($element, array $options) {
    * @return array
    *   Configuration data translated to the requested language.
    */
-  protected function getArrayTranslation(ArrayElement $element, array $options) {
+  protected function getArrayTranslation(TypedConfigListInterface $element, array $options) {
     $translation = array();
     foreach ($element as $key => $property) {
       $value = $this->getElementTranslation($property, $options);
diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
index 8eaad50..0af5df5 100644
--- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
@@ -84,7 +84,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/locale/src/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
index 0c7b9de..5101a0a 100644
--- a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
+++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
@@ -298,7 +298,7 @@ public function testConfigPoFile() {
     foreach ($config_strings as $config_key => $config_string) {
       $wrapper = $locale_config->get($config_key);
       $translation = $wrapper->getTranslation($langcode);
-      $properties = $translation->getProperties();
+      $properties = $translation->getElements();
       $this->assertEqual(count($properties), 1, 'Got the right number of properties with strict translation');
       $this->assertEqual($properties[$config_string[2]]->getValue(), $config_string[1]);
     }
