diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
index 7fc6cc5153..768d993349 100644
--- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
+++ b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php
@@ -78,6 +78,14 @@ public function get($name) {
   /**
    * {@inheritdoc}
    */
+  public function set($name, $value) {
+    $this->get($name)->setValue($value);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getElements() {
     if (!isset($this->elements)) {
       $this->elements = $this->parse();
diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php
index 740fde1fc6..73db292089 100644
--- a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php
+++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\Config\Schema;
 
-use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\DataContainerInterface;
 
 /**
  * Interface for a typed configuration object that contains multiple elements.
@@ -14,7 +14,7 @@
  * 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 {
+interface TypedConfigInterface extends DataContainerInterface {
 
   /**
    * Determines whether the data structure is empty.
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
index 5beeb6f1cd..188b648b2d 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
@@ -20,7 +20,7 @@
    * @param string $name
    *   Configuration object name.
    *
-   * @return \Drupal\Core\TypedData\TraversableTypedDataInterface
+   * @return \Drupal\Core\TypedData\DataContainerInterface
    *   Typed configuration element.
    */
   public function get($name);
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 6396cb8f51..12c4c8f18f 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -18,7 +18,7 @@
  *
  * @ingroup typed_data
  */
-interface ComplexDataInterface extends TraversableTypedDataInterface {
+interface ComplexDataInterface extends DataContainerInterface {
 
   /**
    * Gets the data definition.
@@ -94,12 +94,4 @@ public function getProperties($include_computed = FALSE);
    */
   public function toArray();
 
-  /**
-   * Determines whether the data structure is empty.
-   *
-   * @return bool
-   *   TRUE if the data structure is empty, FALSE otherwise.
-   */
-  public function isEmpty();
-
 }
diff --git a/core/lib/Drupal/Core/TypedData/DataContainerInterface.php b/core/lib/Drupal/Core/TypedData/DataContainerInterface.php
new file mode 100644
index 0000000000..00c91b9792
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataContainerInterface.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for data containers; i.e., data containing other typed data.
+ *
+ * This typed data might be either other containers, like a list, or some
+ * primitive values like a string or an integer typed data.
+ *
+ * This is a common interface for complex data and lists, which both contain
+ * other typed data items. If neither complex data or lists are a good fit for
+ * a certain data structure, this generic interface can be implemented directly.
+ *
+ * @see \Drupal\Core\TypedData\ComplexDataInterface
+ * @see \Drupal\Core\TypedData\ListInterface
+ */
+interface DataContainerInterface extends TypedDataInterface, TraversableTypedDataInterface {
+
+  /**
+   * Gets a child object.
+   *
+   * @param string|int $key
+   *   The key of the child to get; e.g., a property name like 'title' or
+   *   'name', or the index of a list item.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The child object.
+   *
+   * @throws \InvalidArgumentException
+   *   If an invalid property name is given.
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If there is no child object at the requested key and it can not be
+   *   auto-created.
+   */
+  public function get($key);
+
+  /**
+   * Sets a child value.
+   *
+   * @param string|int $key
+   *   The key of the child to get; e.g., a property name like 'title' or
+   *   'name', or the index of a list item.
+   * @param mixed $value
+   *   The value to set, or NULL to unset the child.
+   *
+   * @return $this
+   *
+   * @throws \InvalidArgumentException
+   *   If the specified property does not exist.
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   If there is no child object at the requested key and it can not be
+   *   set there.
+   */
+  public function set($key, $value);
+
+  /**
+   * Determines whether the data structure is empty.
+   *
+   * @return bool
+   *   TRUE if the data structure is empty, FALSE otherwise.
+   */
+  public function isEmpty();
+
+  /**
+   * React to changes to a child object.
+   *
+   * Note that this is invoked after any changes have been applied.
+   *
+   * @param string|int $key
+   *   The key of the child to get; e.g., a property name like 'title' or
+   *   'name', or the index of a list item.
+   */
+  public function onChange($key);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 1dab39a0f7..08b2d8543d 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -15,7 +15,7 @@
  *
  * @ingroup typed_data
  */
-interface ListInterface extends TraversableTypedDataInterface, \ArrayAccess, \Countable {
+interface ListInterface extends DataContainerInterface, \ArrayAccess, \Countable {
 
   /**
    * Gets the data definition.
@@ -26,14 +26,6 @@
   public function getDataDefinition();
 
   /**
-   * Determines whether the list contains any non-empty items.
-   *
-   * @return bool
-   *   TRUE if the list is empty, FALSE otherwise.
-   */
-  public function isEmpty();
-
-  /**
    * Gets the definition of a contained item.
    *
    * @return \Drupal\Core\TypedData\DataDefinitionInterface
@@ -52,7 +44,8 @@ public function getItemDefinition();
    *   exists at that position.
    *
    * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no item can be created.
+   *   If there is no item at the specified index and it can not be
+   *   auto-created.
    */
   public function get($index);
 
@@ -76,7 +69,8 @@ public function get($index);
    *   If the $index is invalid (non-numeric, or pointing to an invalid
    *   position in the list).
    * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   If the complex data structure is unset and no item can be set.
+   *   If there is no item at the specified index and it no item can be set
+   *   there.
    */
   public function set($index, $value);
 
