diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7ec27d40b7674f6feb63950ef76e84b3e3d8106
--- /dev/null
+++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/DecimalItem.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\number\Plugin\field\field_type\DecimalItem.
+ */
+
+namespace Drupal\number\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\Component\Utility\MapArray;
+
+/**
+ * Plugin implementation of the 'number_decimal' field type.
+ *
+ * @FieldType(
+ *   id = "number_decimal",
+ *   module = "number",
+ *   label = @Translation("Decimal"),
+ *   description = @Translation("This field stores a number in the database in a fixed decimal format."),
+ *   settings = {
+ *     "precision" = "10",
+ *     "scale" = "2"
+ *   },
+ *   instance_settings = {
+ *     "min" = "",
+ *     "max" = "",
+ *     "prefix" = "",
+ *     "suffix" = ""
+ *   },
+ *   default_widget = "number",
+ *   default_formatter = "number_decimal"
+ * )
+ */
+class DecimalItem extends NumberItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = array(
+        'type' => 'string',
+        'label' => t('Decimal value'),
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(Field $field) {
+    return array(
+      'columns' => array(
+        'value' => array(
+          'type' => 'numeric',
+          'precision' => $field->settings['precision'],
+          'scale' => $field->settings['scale'],
+          'not null' => FALSE
+        )
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element = array();
+    $settings = $this->getFieldDefinition()->getFieldSettings();
+    $has_data = $this->getInstance()->getField()->hasData();
+
+    $element['precision'] = array(
+      '#type' => 'select',
+      '#title' => t('Precision'),
+      '#options' => MapArray::copyValuesToKeys(range(10, 32)),
+      '#default_value' => $settings['precision'],
+      '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
+      '#disabled' => $has_data,
+    );
+    $element['scale'] = array(
+      '#type' => 'select',
+      '#title' => t('Scale'),
+      '#options' => MapArray::copyValuesToKeys(range(0, 10)),
+      '#default_value' => $settings['scale'],
+      '#description' => t('The number of digits to the right of the decimal.'),
+      '#disabled' => $has_data,
+    );
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSave() {
+    $this->value = round($this->value, $this->getFieldDefinition()->getFieldSetting('scale'));
+  }
+
+}
diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cd5e6eeb1aba3ae2a053593632dc488fd28abdf
--- /dev/null
+++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/FloatItem.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\number\Plugin\field\field_type\FloatItem.
+ */
+
+namespace Drupal\number\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Core\Entity\Field;
+
+/**
+ * Plugin implementation of the 'number_float' field type.
+ *
+ * @FieldType(
+ *   id = "number_float",
+ *   module = "number",
+ *   label = @Translation("Float"),
+ *   description = @Translation("This field stores a number in the database in a floating point format."),
+ *   instance_settings = {
+ *     "min" = "",
+ *     "max" = "",
+ *     "prefix" = "",
+ *     "suffix" = ""
+ *   },
+ *   default_widget = "number",
+ *   default_formatter = "number_decimal"
+ * )
+ */
+class FloatItem extends NumberItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = array(
+        'type' => 'float',
+        'label' => t('float value'),
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(Field $field) {
+    return array(
+      'columns' => array(
+        'value' => array(
+          'type' => 'float',
+          'not null' => FALSE,
+        ),
+      ),
+    );
+  }
+
+}
diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..29f0e34b2681f474dc7f1738f1d42b6ab637ef84
--- /dev/null
+++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/IntegerItem.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\number\Plugin\field\field_type\IntegerItem.
+ */
+
+namespace Drupal\number\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Core\Entity\Field;
+
+/**
+ * Plugin implementation of the 'number_integer' field type.
+ *
+ * @FieldType(
+ *   id = "number_integer",
+ *   module = "number",
+ *   label = @Translation("Integer"),
+ *   description = @Translation("This field stores a number in the database as an integer."),
+ *   instance_settings = {
+ *     "min" = "",
+ *     "max" = "",
+ *     "prefix" = "",
+ *     "suffix" = ""
+ *   },
+ *   default_widget = "number",
+ *   default_formatter = "number_integer"
+ * )
+ */
+class IntegerItem extends NumberItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = array(
+        'type' => 'integer',
+        'label' => t('Integer value'),
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(Field $field) {
+    return array(
+      'columns' => array(
+        'value' => array(
+          'type' => 'int',
+          'not null' => FALSE,
+        ),
+      ),
+    );
+  }
+
+}
diff --git a/core/modules/number/lib/Drupal/number/Plugin/field/field_type/NumberItemBase.php b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/NumberItemBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..326088a2de2638bc455003a8c94a023cf81f5d4a
--- /dev/null
+++ b/core/modules/number/lib/Drupal/number/Plugin/field/field_type/NumberItemBase.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\number\Plugin\field\field_type\NumberItemBase.
+ */
+
+namespace Drupal\number\Plugin\field\field_type;
+
+use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase;
+
+/**
+ * Base class for 'number' configurable field types.
+ */
+abstract class NumberItemBase extends ConfigFieldItemBase {
+
+  /**
+   * Definitions of the contained properties.
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function instanceSettingsForm(array $form, array &$form_state) {
+    $element = array();
+    $settings = $this->getFieldDefinition()->getFieldSettings();
+
+    $element['min'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Minimum'),
+      '#default_value' => $settings['min'],
+      '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
+      '#element_validate' => array('form_validate_number'),
+    );
+    $element['max'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Maximum'),
+      '#default_value' => $settings['max'],
+      '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
+      '#element_validate' => array('form_validate_number'),
+    );
+    $element['prefix'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Prefix'),
+      '#default_value' => $settings['prefix'],
+      '#size' => 60,
+      '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+    );
+    $element['suffix'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Suffix'),
+      '#default_value' => $settings['suffix'],
+      '#size' => 60,
+      '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+    );
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    if (empty($this->value) && (string) $this->value !== '0') {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    $constraint_manager = \Drupal::typedData()->getValidationConstraintManager();
+    $constraints = parent::getConstraints();
+    $settings = $this->getFieldDefinition()->getFieldSettings();
+    $label = $this->getFieldDefinition()->getFieldLabel();
+
+    if (!empty($settings['min'])) {
+      $min = $settings['min'];
+      $constraints[] = $constraint_manager->create('ComplexData', array(
+        'value' => array(
+          'Range' => array(
+            'min' => $min,
+            'minMessage' => t('%name: the value may be no less than %min.', array('%name' => $label, '%min' => $min)),
+          )
+        ),
+      ));
+    }
+
+    if (!empty($settings['max'])) {
+      $max = $settings['max'];
+      $constraints[] = $constraint_manager->create('ComplexData', array(
+        'value' => array(
+          'Range' => array(
+            'max' => $max,
+            'maxMessage' => t('%name: the value may be no greater than %max.', array('%name' => $label, '%max' => $max)),
+          )
+        ),
+      ));
+    }
+
+    return $constraints;
+  }
+
+}
diff --git a/core/modules/number/lib/Drupal/number/Type/DecimalItem.php b/core/modules/number/lib/Drupal/number/Type/DecimalItem.php
deleted file mode 100644
index 83284d48204d138f101a238cc4f06a9b0f61783a..0000000000000000000000000000000000000000
--- a/core/modules/number/lib/Drupal/number/Type/DecimalItem.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\number\Type\DecimalItem.
- */
-
-namespace Drupal\number\Type;
-
-use Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem;
-
-/**
- * Defines the 'number_decimal_field' entity field item.
- */
-class DecimalItem extends LegacyConfigFieldItem {
-
-  /**
-   * Definitions of the contained properties.
-   *
-   * @see DecimalItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        // Decimals are represented as string in PHP.
-        'type' => 'string',
-        'label' => t('Decimal value'),
-      );
-    }
-    return static::$propertyDefinitions;
-  }
-}
diff --git a/core/modules/number/lib/Drupal/number/Type/FloatItem.php b/core/modules/number/lib/Drupal/number/Type/FloatItem.php
deleted file mode 100644
index ea135e5519105eb41833d2a3c856cdc6f6d2710f..0000000000000000000000000000000000000000
--- a/core/modules/number/lib/Drupal/number/Type/FloatItem.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\number\Type\FloatItem.
- */
-
-namespace Drupal\number\Type;
-
-use Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem;
-
-/**
- * Defines the 'number_float_field' entity field item.
- */
-class FloatItem extends LegacyConfigFieldItem {
-
-  /**
-   * Definitions of the contained properties.
-   *
-   * @see FloatItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'float',
-        'label' => t('Float value'),
-      );
-    }
-    return static::$propertyDefinitions;
-  }
-}
diff --git a/core/modules/number/lib/Drupal/number/Type/IntegerItem.php b/core/modules/number/lib/Drupal/number/Type/IntegerItem.php
deleted file mode 100644
index fb7daa0dc776eed52e9638a09121eba9488d2564..0000000000000000000000000000000000000000
--- a/core/modules/number/lib/Drupal/number/Type/IntegerItem.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\number\Type\IntegerItem.
- */
-
-namespace Drupal\number\Type;
-
-use Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem;
-
-/**
- * Defines the 'number_integer_field' entity field item.
- */
-class IntegerItem extends LegacyConfigFieldItem {
-
-  /**
-   * Definitions of the contained properties.
-   *
-   * @see IntegerItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'integer',
-        'label' => t('Integer value'),
-      );
-    }
-    return static::$propertyDefinitions;
-  }
-}
diff --git a/core/modules/number/number.install b/core/modules/number/number.install
deleted file mode 100644
index 3c4ae7f7f173f54b14d4a3b9a37eaf57a149c829..0000000000000000000000000000000000000000
--- a/core/modules/number/number.install
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update, and uninstall functions for the Number module.
- */
-
-/**
- * Implements hook_field_schema().
- */
-function number_field_schema($field) {
-  switch ($field['type']) {
-    case 'number_integer' :
-      $columns = array(
-        'value' => array(
-          'type' => 'int',
-          'not null' => FALSE
-        ),
-      );
-      break;
-
-    case 'number_float' :
-      $columns = array(
-        'value' => array(
-          'type' => 'float',
-          'not null' => FALSE
-        ),
-      );
-      break;
-
-    case 'number_decimal' :
-      $columns = array(
-        'value' => array(
-          'type' => 'numeric',
-          'precision' => $field['settings']['precision'],
-          'scale' => $field['settings']['scale'],
-          'not null' => FALSE
-        ),
-      );
-      break;
-  }
-  return array(
-    'columns' => $columns,
-  );
-}
diff --git a/core/modules/number/number.module b/core/modules/number/number.module
index 5e6bd9240e0a9ed0876a8d8a20353fc1d8b82b8c..9e52fbca2014fe35c85c4a44d78b5c19382894d4 100644
--- a/core/modules/number/number.module
+++ b/core/modules/number/number.module
@@ -5,8 +5,6 @@
  * Defines numeric field types.
  */
 
-use Drupal\Core\Entity\EntityInterface;
-
 /**
  * Implements hook_help().
  */
@@ -19,154 +17,3 @@ function number_help($path, $arg) {
       return $output;
   }
 }
-
-/**
- * Implements hook_field_info().
- */
-function number_field_info() {
-  return array(
-    'number_integer' => array(
-      'label' => t('Integer'),
-      'description' => t('This field stores a number in the database as an integer.'),
-      'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
-      'default_widget' => 'number',
-      'default_formatter' => 'number_integer',
-      'class' => '\Drupal\number\Type\IntegerItem',
-    ),
-    'number_decimal' => array(
-      'label' => t('Decimal'),
-      'description' => t('This field stores a number in the database in a fixed decimal format.'),
-      'settings' => array('precision' => 10, 'scale' => 2),
-      'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
-      'default_widget' => 'number',
-      'default_formatter' => 'number_decimal',
-      'class' => '\Drupal\number\Type\DecimalItem',
-    ),
-    'number_float' => array(
-      'label' => t('Float'),
-      'description' => t('This field stores a number in the database in a floating point format.'),
-      'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
-      'default_widget' => 'number',
-      'default_formatter' => 'number_decimal',
-      'class' => '\Drupal\number\Type\FloatItem',
-    ),
-  );
-}
-
-/**
- * Implements hook_field_settings_form().
- */
-function number_field_settings_form($field, $instance) {
-  $settings = $field['settings'];
-  $form = array();
-
-  if ($field['type'] == 'number_decimal') {
-    $form['precision'] = array(
-      '#type' => 'select',
-      '#title' => t('Precision'),
-      '#options' => drupal_map_assoc(range(10, 32)),
-      '#default_value' => $settings['precision'],
-      '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
-      '#disabled' => $field->hasData(),
-    );
-    $form['scale'] = array(
-      '#type' => 'select',
-      '#title' => t('Scale'),
-      '#options' => drupal_map_assoc(range(0, 10)),
-      '#default_value' => $settings['scale'],
-      '#description' => t('The number of digits to the right of the decimal.'),
-      '#disabled' => $field->hasData(),
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Implements hook_field_instance_settings_form().
- */
-function number_field_instance_settings_form($field, $instance) {
-  $settings = $instance['settings'];
-
-  $form['min'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Minimum'),
-    '#default_value' => $settings['min'],
-    '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
-    '#element_validate' => array('form_validate_number'),
-  );
-  $form['max'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Maximum'),
-    '#default_value' => $settings['max'],
-    '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
-    '#element_validate' => array('form_validate_number'),
-  );
-  $form['prefix'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Prefix'),
-    '#default_value' => $settings['prefix'],
-    '#size' => 60,
-    '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
-  );
-  $form['suffix'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Suffix'),
-    '#default_value' => $settings['suffix'],
-    '#size' => 60,
-    '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
-  );
-
-  return $form;
-}
-
-/**
- * Implements hook_field_validate().
- *
- * Possible error codes:
- * - number_min: The value is less than the allowed minimum value.
- * - number_max: The value is greater than the allowed maximum value.
- */
-function number_field_validate(EntityInterface $entity = NULL, $field, $instance, $langcode, $items, &$errors) {
-  foreach ($items as $delta => $item) {
-    if ($item['value'] != '') {
-      if (is_numeric($instance['settings']['min']) && $item['value'] < $instance['settings']['min']) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'number_min',
-          'message' => t('%name: the value may be no less than %min.', array('%name' => $instance['label'], '%min' => $instance['settings']['min'])),
-        );
-      }
-      if (is_numeric($instance['settings']['max']) && $item['value'] > $instance['settings']['max']) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'number_max',
-          'message' => t('%name: the value may be no greater than %max.', array('%name' => $instance['label'], '%max' => $instance['settings']['max'])),
-        );
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_presave().
- */
-function number_field_presave(EntityInterface $entity, $field, $instance, $langcode, &$items) {
-  if ($field['type'] == 'number_decimal') {
-    // Let PHP round the value to ensure consistent behavior across storage
-    // backends.
-    foreach ($items as $delta => $item) {
-      if (isset($item['value'])) {
-        $items[$delta]['value'] = round($item['value'], $field['settings']['scale']);
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_is_empty().
- */
-function number_field_is_empty($item, $field_type) {
-  if (empty($item['value']) && (string) $item['value'] !== '0') {
-    return TRUE;
-  }
-  return FALSE;
-}