diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
new file mode 100644
index 0000000..f432cac
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\AllowedValuesInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for retrieving allowed values.
+ *
+ * While allowed values define the values that are allowed to be set by a user,
+ * the available values may be used to get the list of possible values that may
+ * be already set on an object.
+ * For example, you cannot set the "authenticated" role on the user role field,
+ * but still it is one of the available values for the field. Thus allowed
+ * values would be used during any editing context, while available values
+ * would be used when e.g. filtering for existing values.
+ */
+interface AllowedValuesInterface {
+
+  /**
+   * Returns an array of allowed values.
+   *
+   * @param object $account
+   *   (optional) The user account for which to generate the allowed values.
+   *
+   * @return array
+   *   An array allowed values.
+   */
+  public function getAllowedValues($account = NULL);
+
+  /**
+   * Returns an array of allowed options.
+   *
+   * @param object $account
+   *   (optional) The user account for which to generate the allowed options.
+   *
+   * @return
+   *   The array of allowed options for the object. Array keys are the values as
+   *   expected by the object. Array values are the labels to display; e.g.,
+   *   within a widget. The labels should NOT be sanitized.
+   *
+   * @see Drupal\Core\TypedData::AllowedValuesInterface::getAllowedValues()
+   */
+  public function getAllowedOptions($account = NULL);
+
+  /**
+   * Returns an array of available values.
+   *
+   * @param object $account
+   *   (optional) The user account for which to generate the available values.
+   *
+   * @return array
+   *   An array of available values.
+   */
+  public function getAvailableValues($account = NULL);
+
+  /**
+   * Returns an array of available options.
+   *
+   * @param object $account
+   *   (optional) The user account for which to generate the available options.
+   *
+   * @return
+   *   The array of allowed options for the object. Array keys are the values as
+   *   expected by the object. Array values are the labels to display; e.g.,
+   *   within a widget. The labels should NOT be sanitized.
+   *
+   * @see Drupal\Core\TypedData::AllowedValuesInterface::getAllowedOptions()
+   */
+  public function getAvailableOptions($account = NULL);
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 195e32d..fd14555 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -389,6 +389,14 @@ public function getConstraints($definition) {
     if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
       $constraints[] = $this->createValidationConstraint('NotNull', array());
     }
+
+    // If the definition provides a class check for further validation criteria.
+    $class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
+    // Check if the class provides allowed values.
+    if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
+      $constraints[] = $this->createValidationConstraint('AllowedValues', array());
+    }
+
     return $constraints;
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
index 106f2d0..83ca0de 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
@@ -91,4 +91,14 @@ public function getPropertyName() {
   public function getPropertyValue($container) {
     return $this->typedData->getValue();
   }
+
+  /**
+   * Returns the typed data object.
+   *
+   * @return TypedDataInterface
+   *   The typed data object.
+   */
+  public function getTypedData() {
+    return $this->typedData;
+  }
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
new file mode 100644
index 0000000..b85a9a7
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Constraint\AllowedValuesConstraint.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraints\Choice;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Checks for the value being allowed.
+ *
+ * @Plugin(
+ *   id = "AllowedValues",
+ *   label = @Translation("AllowedValues", context = "Validation")
+ * )
+ *
+ * @see \Drupal\Core\TypedData\AllowedValuesInterface
+ */
+class AllowedValuesConstraint extends Choice {
+
+  public $minMessage = 'You must select at least %limit choice.|You must select at least %limit choices.';
+  public $maxMessage = 'You must select at most %limit choice.|You must select at most %limit choices.';
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
new file mode 100644
index 0000000..b538eec
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraintValidator.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Drupal\Core\TypedData\AllowedValuesInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\ChoiceValidator;
+
+/**
+ * Validates the AllowedValues constraint.
+ */
+class AllowedValuesConstraintValidator extends ChoiceValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($value, Constraint $constraint) {
+    if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
+      $allowed_values = $this->context->getMetadata()->getTypedData()->getAllowedValues();
+      $constraint->setAllowedValues($allowed_values);
+    }
+    return parent::validate($value, $constraint);
+  }
+}
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index 30aec3f..542e2bf 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -19,7 +19,7 @@ class EditTestBase extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'text', 'edit');
+  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'filter', 'user', 'text', 'edit');
   /**
    * Sets the default field storage backend for fields created during tests.
    */
@@ -28,7 +28,7 @@ function setUp() {
 
     $this->installSchema('system', 'variable');
     $this->installSchema('entity_test', array('entity_test', 'entity_test_rev'));
-    $this->installConfig(array('field'));
+    $this->installConfig(array('field', 'filter'));
   }
 
   /**
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 0d8f737..37a906a 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -9,6 +9,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Template\Attribute;
 use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+use Drupal\Core\TypedData\Primitive;
 
 /**
  * Non-HTML markup language filters that generate HTML.
@@ -111,6 +112,19 @@ function filter_element_info() {
 }
 
 /**
+ * Implements hook_data_type_info().
+ */
+function filter_data_type_info() {
+  return array(
+    'filter_format' => array(
+      'label' => t('Filter format'),
+      'class' => 'Drupal\filter\Type\FilterFormat',
+      'primitive type' => Primitive::STRING,
+    ),
+  );
+}
+
+/**
  * Implements hook_menu().
  */
 function filter_menu() {
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
index 3918ea9..a066cad 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
@@ -106,4 +106,66 @@ function testFilterFormatAPI() {
     );
   }
 
+  /**
+   * Tests the function of the typed data type.
+   */
+  function testTypedDataAPI() {
+    $definition = array('type' => 'filter_format');
+    $data = typed_data()->create($definition);
+
+    if ($this->assertTrue($data instanceof \Drupal\Core\TypedData\AllowedValuesInterface, 'Typed data object implements \Drupal\Core\TypedData\AllowedValuesInterface')) {
+      $this->checkPermissions(array(), TRUE);
+      $filtered_html_user = $this->drupalCreateUser(array(
+        filter_permission_name(filter_format_load('filtered_html')),
+      ));
+      // Test with anonymous user.
+      $GLOBALS['user'] = drupal_anonymous_user();
+
+      $available_values = $data->getAvailableValues();
+      $this->assertEqual(
+        $available_values,
+        array('filtered_html', 'full_html', 'plain_text'),
+        'Expected available format values found'
+      );
+      $available_options = $data->getAvailableOptions();
+      $expected_available_options = array(
+        'filtered_html' => 'Filtered HTML',
+        'full_html' => 'Full HTML',
+        'plain_text' => 'Plain text',
+      );
+      $this->assertEqual($available_options, $expected_available_options);
+      $allowed_values = $data->getAllowedValues();
+      $this->assertEqual($allowed_values, array('plain_text'));
+      $allowed_options = $data->getAllowedOptions();
+      $this->assertEqual($allowed_options, array('plain_text' => 'Plain text'));
+
+      $data->setValue('foo');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 1, "Validation violation about for format 'foo' found");
+
+      $data->setValue('plain_text');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 0, "No validation violation for format 'plain_text' found");
+
+      // Anonymous doesn't have access to the 'filtered_html' format.
+      $data->setValue('filtered_html');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 1, "Validation violation for protected format 'filtered_html' found.");
+
+      // Set user with access to 'filtered_html' format.
+      $GLOBALS['user'] = $filtered_html_user;
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 0, "No validation violation for accessible format 'filtered_html' found.");
+
+      $allowed_values = $data->getAllowedValues();
+      $this->assertEqual($allowed_values, array('filtered_html', 'plain_text'));
+      $allowed_options = $data->getAllowedOptions();
+      $expected_allowed_options = array(
+        'filtered_html' => 'Filtered HTML',
+        'plain_text' => 'Plain text',
+      );
+      $this->assertEqual($allowed_options, $expected_allowed_options);
+    }
+  }
+
 }
diff --git a/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php
new file mode 100644
index 0000000..a1326ad
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Type\FilterFormat.
+ */
+
+namespace Drupal\filter\Type;
+
+use Drupal\Core\TypedData\Type\String;
+use Drupal\Core\TypedData\AllowedValuesInterface;
+
+/**
+ * The filter format data type.
+ */
+class FilterFormat extends String implements AllowedValuesInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableValues($account = NULL) {
+    return array_keys($this->getAvailableOptions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableOptions($account = NULL) {
+    $values = array();
+    foreach (filter_formats() as $format) {
+      $values[$format->id()] = $format->label();
+    }
+    return $values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllowedValues($account = NULL) {
+    return array_keys($this->getAllowedOptions($account));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllowedOptions($user = NULL) {
+    $user = empty($user) ? $GLOBALS['user'] : $user;
+    $values = array();
+    // @todo: Avoid calling functionsm but move to injected dependencies.
+    foreach (filter_formats($user) as $format) {
+      $values[$format->id()] = $format->label();
+    }
+    return $values;
+  }
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index 2d73a24..38869f2 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -30,7 +30,7 @@
    *
    * @var array
    */
-  public static $modules = array('entity', 'entity_test', 'entity_reference', 'field', 'field_sql_storage', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user');
+  public static $modules = array('entity', 'entity_test', 'entity_reference', 'field', 'field_sql_storage', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user', 'filter');
 
   /**
    * The mock serializer.
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
index 1d9bff6..c147d61 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
@@ -16,7 +16,7 @@
    *
    * @var array
    */
-  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage');
+  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'filter', 'field_sql_storage');
 
   protected function setUp() {
     parent::setUp();
diff --git a/core/modules/text/lib/Drupal/text/Type/TextItem.php b/core/modules/text/lib/Drupal/text/Type/TextItem.php
index b71812f..9e5e7a6 100644
--- a/core/modules/text/lib/Drupal/text/Type/TextItem.php
+++ b/core/modules/text/lib/Drupal/text/Type/TextItem.php
@@ -34,7 +34,7 @@ public function getPropertyDefinitions() {
         'label' => t('Text value'),
       );
       static::$propertyDefinitions['format'] = array(
-        'type' => 'string',
+        'type' => 'filter_format',
         'label' => t('Text format'),
       );
       static::$propertyDefinitions['processed'] = array(
