diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index 49418a0..9c8cb5e 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -10,7 +10,7 @@
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\user\Plugin\Core\Entity\User;
 use Drupal\Core\TypedData\ContextAwareInterface;
-use Drupal\Core\TypedData\ContextAwareTypedData;
+use Drupal\Core\TypedData\ItemList;
 use Drupal\Core\TypedData\TypedDataInterface;
 use ArrayIterator;
 use IteratorAggregate;
@@ -27,7 +27,7 @@
  *
  * @see \Drupal\Core\Entity\Field\FieldInterface
  */
-class Field extends ContextAwareTypedData implements IteratorAggregate, FieldInterface {
+class Field extends ItemList implements IteratorAggregate, FieldInterface {
 
   /**
    * Numerically indexed array of field items, implementing the
@@ -50,7 +50,7 @@ public function __construct(array $definition, $name = NULL, ContextAwareInterfa
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
+   * Overrides \Drupal\Core\TypedData\ItemList::getValue().
    */
   public function getValue() {
     if (isset($this->list)) {
@@ -68,147 +68,6 @@ public function getValue() {
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
-   *
-   * @param array|null $values
-   *   An array of values of the field items, or NULL to unset the field.
-   */
-  public function setValue($values) {
-    if (!isset($values) || $values === array()) {
-      $this->list = $values;
-    }
-    else {
-      // Support passing in only the value of the first item.
-      if (!is_array($values) || !is_numeric(current(array_keys($values)))) {
-        $values = array(0 => $values);
-      }
-
-      // Clear the values of properties for which no value has been passed.
-      if (isset($this->list)) {
-        $this->list = array_intersect_key($this->list, $values);
-      }
-
-      // Set the values.
-      foreach ($values as $delta => $value) {
-        if (!is_numeric($delta)) {
-          throw new InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
-        }
-        elseif (!isset($this->list[$delta])) {
-          $this->list[$delta] = $this->createItem($delta, $value);
-        }
-        else {
-          $this->list[$delta]->setValue($value);
-        }
-      }
-    }
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getString().
-   */
-  public function getString() {
-    $strings = array();
-    if (isset($this->list)) {
-      foreach ($this->list as $item) {
-        $strings[] = $item->getString();
-      }
-      return implode(', ', array_filter($strings));
-    }
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
-   */
-  public function getConstraints() {
-    // Apply the constraints to the list items only.
-    return array();
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetExists().
-   */
-  public function offsetExists($offset) {
-    return isset($this->list) && array_key_exists($offset, $this->list);
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetUnset().
-   */
-  public function offsetUnset($offset) {
-    if (isset($this->list)) {
-      unset($this->list[$offset]);
-    }
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetGet().
-   */
-  public function offsetGet($offset) {
-    if (!is_numeric($offset)) {
-      throw new InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
-    }
-    // Allow getting not yet existing items as well.
-    // @todo: Maybe add a public createItem() method in addition?
-    elseif (!isset($this->list[$offset])) {
-      $this->list[$offset] = $this->createItem($offset);
-    }
-    return $this->list[$offset];
-  }
-
-  /**
-   * Helper for creating a list item object.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   */
-  protected function createItem($offset = 0, $value = NULL) {
-    return typed_data()->getPropertyInstance($this, $offset, $value);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
-   */
-  public function getItemDefinition() {
-    return array('list' => FALSE) + $this->definition;
-  }
-
-  /**
-   * Implements \ArrayAccess::offsetSet().
-   */
-  public function offsetSet($offset, $value) {
-    if (!isset($offset)) {
-      // The [] operator has been used so point at a new entry.
-      $offset = $this->list ? max(array_keys($this->list)) + 1 : 0;
-    }
-    if (is_numeric($offset)) {
-      // Support setting values via typed data objects.
-      if ($value instanceof TypedDataInterface) {
-        $value = $value->getValue();
-      }
-      $this->offsetGet($offset)->setValue($value);
-    }
-    else {
-      throw new InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
-    }
-  }
-
-  /**
-   * Implements \IteratorAggregate::getIterator().
-   */
-  public function getIterator() {
-    if (isset($this->list)) {
-      return new ArrayIterator($this->list);
-    }
-    return new ArrayIterator(array());
-  }
-
-  /**
-   * Implements \Countable::count().
-   */
-  public function count() {
-    return isset($this->list) ? count($this->list) : 0;
-  }
-
-  /**
    * Implements \Drupal\Core\Entity\Field\FieldInterface::getPropertyDefinition().
    */
   public function getPropertyDefinition($name) {
@@ -258,34 +117,6 @@ public function __unset($property_name) {
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\ListInterface::isEmpty().
-   */
-  public function isEmpty() {
-    if (isset($this->list)) {
-      foreach ($this->list as $item) {
-        if (!$item->isEmpty()) {
-          return FALSE;
-        }
-      }
-    }
-    return TRUE;
-  }
-
-  /**
-   * Magic method: Implements a deep clone.
-   */
-  public function __clone() {
-    if (isset($this->list)) {
-      foreach ($this->list as $delta => $item) {
-        $this->list[$delta] = clone $item;
-        if ($item instanceof ContextAwareInterface) {
-          $this->list[$delta]->setContext($delta, $this);
-        }
-      }
-    }
-  }
-
-  /**
    * Implements \Drupal\Core\TypedData\AccessibleInterface::access().
    */
   public function access($operation = 'view', User $account = NULL) {
diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/ItemList.php
new file mode 100644
index 0000000..040579c
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ItemList.php
@@ -0,0 +1,204 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\List.
+ */
+
+namespace Drupal\Core\TypedData;
+
+use ArrayIterator;
+use IteratorAggregate;
+use InvalidArgumentException;
+
+/**
+ * A generic list class.
+ *
+ * This class can serve as list for any type of items.
+ * Note: The class cannot be called "List" as list is a reserved PHP keyword.
+ */
+class ItemList extends ContextAwareTypedData implements IteratorAggregate, ListInterface {
+
+  /**
+   * Numerically indexed array items.
+   *
+   * @var array
+   */
+  protected $list = array();
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
+   */
+  public function getValue() {
+    return $this->list;
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
+   *
+   * @param array|null $values
+   *   An array of values of the field items, or NULL to unset the field.
+   */
+  public function setValue($values) {
+    if (!isset($values) || $values === array()) {
+      $this->list = $values;
+    }
+    else {
+      // Support passing in only the value of the first item.
+      if (!is_array($values) || !is_numeric(current(array_keys($values)))) {
+        $values = array(0 => $values);
+      }
+
+      // Clear the values of properties for which no value has been passed.
+      if (isset($this->list)) {
+        $this->list = array_intersect_key($this->list, $values);
+      }
+
+      // Set the values.
+      foreach ($values as $delta => $value) {
+        if (!is_numeric($delta)) {
+          throw new InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
+        }
+        elseif (!isset($this->list[$delta])) {
+          $this->list[$delta] = $this->createItem($delta, $value);
+        }
+        else {
+          $this->list[$delta]->setValue($value);
+        }
+      }
+    }
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getString().
+   */
+  public function getString() {
+    $strings = array();
+    if (isset($this->list)) {
+      foreach ($this->list as $item) {
+        $strings[] = $item->getString();
+      }
+      return implode(', ', array_filter($strings));
+    }
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
+   */
+  public function getConstraints() {
+    // Apply the constraints to the list items only.
+    return array();
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetExists().
+   */
+  public function offsetExists($offset) {
+    return isset($this->list) && array_key_exists($offset, $this->list) && $this->offsetGet($offset)->getValue() !== NULL;
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetUnset().
+   */
+  public function offsetUnset($offset) {
+    if (isset($this->list)) {
+      unset($this->list[$offset]);
+    }
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetGet().
+   */
+  public function offsetGet($offset) {
+    if (!is_numeric($offset)) {
+      throw new InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
+    }
+    // Allow getting not yet existing items as well.
+    // @todo: Maybe add a public createItem() method in addition?
+    elseif (!isset($this->list[$offset])) {
+      $this->list[$offset] = $this->createItem($offset);
+    }
+    return $this->list[$offset];
+  }
+
+  /**
+   * Helper for creating a list item object.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected function createItem($offset = 0, $value = NULL) {
+    return typed_data()->getPropertyInstance($this, $offset, $value);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
+   */
+  public function getItemDefinition() {
+    return array('list' => FALSE) + $this->definition;
+  }
+
+  /**
+   * Implements \ArrayAccess::offsetSet().
+   */
+  public function offsetSet($offset, $value) {
+    if (!isset($offset)) {
+      // The [] operator has been used so point at a new entry.
+      $offset = $this->list ? max(array_keys($this->list)) + 1 : 0;
+    }
+    if (is_numeric($offset)) {
+      // Support setting values via typed data objects.
+      if ($value instanceof TypedDataInterface) {
+        $value = $value->getValue();
+      }
+      $this->offsetGet($offset)->setValue($value);
+    }
+    else {
+      throw new InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
+    }
+  }
+
+  /**
+   * Implements \IteratorAggregate::getIterator().
+   */
+  public function getIterator() {
+    if (isset($this->list)) {
+      return new ArrayIterator($this->list);
+    }
+    return new ArrayIterator(array());
+  }
+
+  /**
+   * Implements \Countable::count().
+   */
+  public function count() {
+    return isset($this->list) ? count($this->list) : 0;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ListInterface::isEmpty().
+   */
+  public function isEmpty() {
+    if (isset($this->list)) {
+      foreach ($this->list as $item) {
+        if (!$item->isEmpty()) {
+          return FALSE;
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Magic method: Implements a deep clone.
+   */
+  public function __clone() {
+    if (isset($this->list)) {
+      foreach ($this->list as $delta => $item) {
+        $this->list[$delta] = clone $item;
+        if ($item instanceof ContextAwareInterface) {
+          $this->list[$delta]->setContext($delta, $this);
+        }
+      }
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Any.php b/core/lib/Drupal/Core/TypedData/Type/Any.php
new file mode 100644
index 0000000..21afa99
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/Any.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\Any.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * The "any" data type.
+ *
+ * The "any" data type does not implement a list or complex data interface, nor
+ * is it mappable to any primitive type. Thus, it may contain any PHP data for
+ * which no further metadata is available.
+ */
+class Any extends TypedData {
+
+  /**
+   * The data value.
+   *
+   * @var mixed
+   */
+  protected $value;
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Map.php b/core/lib/Drupal/Core/TypedData/Type/Map.php
new file mode 100644
index 0000000..201f18f
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/Map.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\Map.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\ContextAwareTypedData;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use ArrayIterator;
+use IteratorAggregate;
+
+/**
+ * The "map" data type.
+ *
+ * The "map" data type represent a simple complex data type, e.g. for
+ * representing associative arrays. It can also serve as base class for any
+ * complex data type.
+ *
+ * By default there is no metadata for contained properties. Extending classes
+ * may want to override Map::getPropertyDefinitions() to define it.
+ */
+class Map extends ContextAwareTypedData implements IteratorAggregate, ComplexDataInterface {
+
+  /**
+   * An array of values for the contained properties.
+   *
+   * @var array
+   */
+  protected $values = array();
+
+  /**
+   * The array of properties, each implementing the TypedDataInterface.
+   *
+   * @var array
+   */
+  protected $properties;
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   */
+  public function getPropertyDefinitions() {
+    $definitions = array();
+    foreach ($this->values as $name => $value) {
+      $definitions[$name] = array(
+        'type' => 'any',
+      );
+    }
+    return $definitions;
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
+   */
+  public function getValue() {
+    return $this->values;
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
+   *
+   * @param array|null $values
+   *   An array of property values.
+   */
+  public function setValue($values) {
+    if (isset($values) && !is_array($values)) {
+      throw new \InvalidArgumentException("Invalid values given. Values must be represented as an associative array.");
+    }
+    $this->values = $values;
+    unset($this->properties);
+  }
+
+  /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getString().
+   */
+  public function getString() {
+    $strings = array();
+    foreach ($this->getProperties() as $property) {
+      $strings[] = $property->getString();
+    }
+    return implode(', ', array_filter($strings));
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::get().
+   */
+  public function get($property_name) {
+    if (!$this->getPropertyDefinition($property_name)) {
+      throw new InvalidArgumentException('Property ' . check_plain($property_name) . ' is unknown.');
+    }
+    if (!isset($this->properties[$property_name])) {
+      $this->properties[$property_name] = typed_data()->getPropertyInstance($this, $property_name, isset($this->values[$property_name]) ? $this->values[$property_name] : NULL);
+    }
+    return $this->properties[$property_name];
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
+   */
+  public function set($property_name, $value) {
+    $this->get($property_name)->setValue($value);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties().
+   */
+  public function getProperties($include_computed = FALSE) {
+    $properties = array();
+    foreach ($this->getPropertyDefinitions() as $name => $definition) {
+      if ($include_computed || empty($definition['computed'])) {
+        $properties[$name] = $this->get($name);
+      }
+    }
+    return $properties;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
+   */
+  public function getPropertyValues() {
+    $values = array();
+    foreach ($this->getProperties() as $name => $property) {
+      $values[$name] = $property->getValue();
+    }
+    return $values;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
+   */
+  public function setPropertyValues($values) {
+    foreach ($values as $name => $value) {
+      $this->get($name)->setValue($value);
+    }
+  }
+
+  /**
+   * Implements \IteratorAggregate::getIterator().
+   */
+  public function getIterator() {
+    return new ArrayIterator($this->getProperties());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
+   */
+  public function getPropertyDefinition($name) {
+    $definitions = $this->getPropertyDefinitions();
+    if (isset($definitions[$name])) {
+      return $definitions[$name];
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
+   */
+  public function isEmpty() {
+    foreach ($this->getProperties() as $property) {
+      if ($property->getValue() !== NULL) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Magic method: Implements a deep clone.
+   */
+  public function __clone() {
+    foreach ($this->getProperties() as $name => $property) {
+      $this->properties[$name] = clone $property;
+      if ($property instanceof ContextAwareInterface) {
+        $this->properties[$name]->setContext($name, $this);
+      }
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 8b5306d..d81dc30 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\TypedData;
 
 use InvalidArgumentException;
+use Drupal\Component\Plugin\Discovery\ProcessDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\HookDiscovery;
@@ -44,11 +45,24 @@ class TypedDataManager extends PluginManagerBase {
   protected $prototypes = array();
 
   public function __construct() {
-    $this->discovery = new CacheDecorator(new HookDiscovery('data_type_info'), 'typed_data:types');
+    $this->discovery = new HookDiscovery('data_type_info');
+    $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
+    $this->discovery = new CacheDecorator($this->discovery, 'typed_data:types');
+
     $this->factory = new TypedDataFactory($this->discovery);
   }
 
   /**
+   * Process definition callback for the ProcessDecorator.
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    // Provide a general list class by default.
+    if (!isset($definition['list class'])) {
+      $definition['list class'] = '\Drupal\Core\TypedData\ItemList';
+    }
+  }
+
+  /**
    * Implements \Drupal\Component\Plugin\PluginManagerInterface::createInstance().
    *
    * @param string $plugin_id
@@ -191,14 +205,20 @@ public function getInstance(array $options) {
    * @see \Drupal\Core\TypedData\TypedDataManager::create()
    */
   public function getPropertyInstance(ContextAwareInterface $object, $property_name, $value = NULL) {
-    $key = $object->getRoot()->getType() . ':' . $object->getPropertyPath() . '.';
-    // If we are creating list items, we always use 0 in the key as all list
-    // items look the same.
-    $key .= is_numeric($property_name) ? 0 : $property_name;
+    if ($root = $object->getRoot()) {
+      $key = $root->getType() . ':' . $object->getPropertyPath() . '.';
+      // If we are creating list items, we always use 0 in the key as all list
+      // items look the same.
+      $key .= is_numeric($property_name) ? 0 : $property_name;    }
+    else {
+      // Missing context, thus we cannot determine a unique key for prototyping.
+      // Fall back to create a new prototype on each call.
+      $key = FALSE;
+    }
 
     // Make sure we have a prototype. Then, clone the prototype and set object
     // specific values, i.e. the value and the context.
-    if (!isset($this->prototypes[$key])) {
+    if (!isset($this->prototypes[$key]) || !$key) {
       // Create the initial prototype. For that we need to fetch the definition
       // of the to be created property instance from the parent.
       if ($object instanceof ComplexDataInterface) {
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 484ee93..3dfee74 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -158,7 +158,8 @@ function hook_cron() {
  *     \Drupal\Core\TypedData\TypedDataInterface.
  *   - list class: (optional) A typed data class used for wrapping multiple
  *     data items of the type. Must implement the
- *     \Drupal\Core\TypedData\ListInterface.
+ *     \Drupal\Core\TypedData\ListInterface. Defaults to
+ *     \Drupal\Core\TypedData\ItemList;
  *   - primitive type: (optional) Maps the data type to one of the pre-defined
  *     primitive types in \Drupal\Core\TypedData\Primitive. If set, it must be
  *     a constant defined by \Drupal\Core\TypedData\Primitive such as
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 1d7671b..a7b5e88 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2229,6 +2229,14 @@ function system_data_type_info() {
       'class' => '\Drupal\Core\TypedData\Type\Binary',
       'primitive type' => Primitive::BINARY,
     ),
+    'map' => array(
+      'label' => t('Map'),
+      'class' =>  '\Drupal\Core\TypedData\Type\Map',
+    ),
+    'any' => array(
+      'label' => t('Any data'),
+      'class' =>  '\Drupal\Core\TypedData\Type\Any',
+    ),
     'language' => array(
       'label' => t('Language'),
       'description' => t('A language object.'),
