diff --git a/core/modules/system/system.tokens.inc b/core/modules/system/system.tokens.inc
index ff78335..1edd1b8 100644
--- a/core/modules/system/system.tokens.inc
+++ b/core/modules/system/system.tokens.inc
@@ -9,11 +9,21 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
  * Implements hook_token_info().
  */
 function system_token_info() {
+  // Derive all typed data data type plugins as token types.
+  foreach (\Drupal::typedDataManager()->getDefinitions() as $plugin_id => $definition) {
+    $types['typed_data:' . $plugin_id] = array(
+      'name' => $definition['label'],
+      'description' => $definition['description'],
+    );
+  }
+
   $types['site'] = array(
     'name' => t("Site information"),
     'description' => t("Tokens for site-wide settings and other global information."),
@@ -89,6 +99,30 @@ function system_token_info() {
  */
 function system_tokens($type, $tokens, array $data = array(), array $options = array()) {
   $token_service = \Drupal::token();
+  $replacements = array();
+
+  // Replace typed data tokens.
+  if (strpos($type, 'typed_data:') === 0 && isset($data[$type])) {
+    $data = $data[$type];
+
+    if (!($data instanceof TypedDataInterface)) {
+      // @todo We should probably not throw exceptions in this hook implementation.
+      throw new \RuntimeException(sprintf('$data[%s] is marked as typed data, but does not implement \Drupal\Core\TypedData\TypedDataInterface.', $type));
+    }
+
+    if ($data instanceof ComplexDataInterface) {
+      foreach ($tokens as $child_name => $original) {
+        $child_data = $data->get($child_name);
+        $child_type = $child_data->getDataDefinition()->getDataType();
+        $child_tokens = $token_service->findWithPrefix($tokens, $child_name);
+        if ($child_tokens) {
+          $replacements += $token_service->generate($type, $child_tokens, array(
+            $child_type => $child_data,
+          ), $options);
+        }
+      }
+    }
+  }
 
   $url_options = array('absolute' => TRUE);
   if (isset($options['langcode'])) {
@@ -100,8 +134,6 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a
   }
   $sanitize = !empty($options['sanitize']);
 
-  $replacements = array();
-
   if ($type == 'site') {
     foreach ($tokens as $name => $original) {
       switch ($name) {
