From cf0959654477cfeb0fc6ee8d30c418b166a5a7b6 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Thu, 25 Apr 2024 15:48:19 +0100
Subject: [PATCH] Issue #3420981 by kim.pepper, quietone, mstrelan, Berdir,
 alexpott: Convert FieldType plugin discovery to attributes

---
 .../Drupal/Core/Field/Attribute/FieldType.php | 87 +++++++++++++++++++
 .../Core/Field/FieldTypePluginManager.php     | 11 ++-
 .../Plugin/Field/FieldType/BooleanItem.php    | 18 ++--
 .../Plugin/Field/FieldType/ChangedItem.php    | 23 ++---
 .../Plugin/Field/FieldType/CreatedItem.php    | 20 +++--
 .../Plugin/Field/FieldType/DecimalItem.php    | 30 +++----
 .../Plugin/Field/FieldType/EmailItem.php      | 16 ++--
 .../Field/FieldType/EntityReferenceItem.php   | 21 ++---
 .../Plugin/Field/FieldType/FloatItem.php      | 29 ++++---
 .../Plugin/Field/FieldType/IntegerItem.php    | 28 +++---
 .../Plugin/Field/FieldType/LanguageItem.php   | 35 ++++----
 .../Field/Plugin/Field/FieldType/MapItem.php  | 20 +++--
 .../Plugin/Field/FieldType/PasswordItem.php   | 14 +--
 .../Plugin/Field/FieldType/StringItem.php     | 29 ++++---
 .../Plugin/Field/FieldType/StringLongItem.php | 27 +++---
 .../Plugin/Field/FieldType/TimestampItem.php  | 47 +++++-----
 .../Field/Plugin/Field/FieldType/UriItem.php  | 19 ++--
 .../Field/Plugin/Field/FieldType/UuidItem.php | 19 ++--
 .../Plugin/Field/FieldType/CommentItem.php    | 27 +++---
 .../Plugin/Field/FieldType/DateTimeItem.php   | 34 ++++----
 .../Plugin/Field/FieldType/DateRangeItem.php  | 29 ++++---
 .../FieldType/EditorTestTextLongItem.php      | 17 ++--
 core/modules/field/field.api.php              |  4 +-
 .../Plugin/Field/FieldType/HiddenTestItem.php | 20 +++--
 .../src/Plugin/Field/FieldType/TestItem.php   | 18 ++--
 .../FieldType/TestItemWithDependencies.php    | 25 +++---
 .../TestItemWithMultipleDescriptions.php      | 26 +++---
 .../TestItemWithPreconfiguredOptions.php      | 16 ++--
 .../TestItemWithSingleDescription.php         | 20 +++--
 .../Plugin/Field/FieldType/TestObjectItem.php | 19 ++--
 .../src/Plugin/Field/FieldType/FileItem.php   | 28 +++---
 .../Plugin/Field/FieldType/FileUriItem.php    | 19 ++--
 .../src/Plugin/Field/FieldType/ImageItem.php  | 69 ++++++++-------
 .../Plugin/Field/FieldType/DummyAjaxItem.php  | 18 ++--
 .../Field/FieldType/LayoutSectionItem.php     | 19 ++--
 .../src/Plugin/Field/FieldType/LinkItem.php   | 25 +++---
 .../FieldType/EntityReferenceItemSubclass.php | 22 ++---
 .../Plugin/Field/FieldType/ListFloatItem.php  | 26 +++---
 .../Field/FieldType/ListIntegerItem.php       | 26 +++---
 .../Plugin/Field/FieldType/ListStringItem.php | 26 +++---
 .../src/Plugin/Field/FieldType/PathItem.php   | 23 ++---
 .../FieldType/AutoIncrementingTestItem.php    | 15 ++--
 .../Field/FieldType/ChangedTestItem.php       | 18 ++--
 .../ComputedTestCacheableStringItem.php       | 18 ++--
 .../Plugin/Field/FieldType/FieldTestItem.php  | 12 +--
 .../InternalPropertyTestFieldItem.php         | 16 ++--
 .../Plugin/Field/FieldType/SerializedItem.php | 12 +--
 .../FieldType/SerializedPropertyItem.php      | 16 ++--
 .../src/Plugin/Field/FieldType/ShapeItem.php  | 15 ++--
 .../Field/FieldType/ShapeItemRequired.php     | 13 +--
 .../SingleInternalPropertyTestFieldItem.php   | 16 ++--
 .../Field/FieldType/MultiValueTestItem.php    | 15 ++--
 .../Plugin/Field/FieldType/TelephoneItem.php  | 18 ++--
 .../src/Plugin/Field/FieldType/TextItem.php   | 31 +++----
 .../Plugin/Field/FieldType/TextLongItem.php   | 29 ++++---
 .../Field/FieldType/TextWithSummaryItem.php   | 32 +++----
 56 files changed, 733 insertions(+), 592 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Field/Attribute/FieldType.php

diff --git a/core/lib/Drupal/Core/Field/Attribute/FieldType.php b/core/lib/Drupal/Core/Field/Attribute/FieldType.php
new file mode 100644
index 000000000000..2a2196f51ef5
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Attribute/FieldType.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Field\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a FieldType attribute.
+ *
+ * Additional attribute keys for field types can be defined in
+ * hook_field_info_alter().
+ *
+ * @ingroup field_types
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class FieldType extends Plugin {
+
+  /**
+   * Constructs a FieldType attribute.
+   *
+   * @param string $id
+   *   The plugin ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
+   *   The human-readable name of the field type.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|array|null $description
+   *   (optional) A short human-readable description for the field type.
+   * @param string $category
+   *   (optional) The category under which the field type should be listed in
+   *   the UI.
+   * @param int $weight
+   *   (optional) The weight of the field type.
+   * @param string|null $default_widget
+   *   (optional) The plugin_id of the default widget for this field type.
+   *   This widget must be available whenever the field type is available (i.e.
+   *   provided by the field type module, or by a module the field type module
+   *   depends on).
+   * @param string|null $default_formatter
+   *   (optional) The plugin_id of the default formatter for this field type.
+   *   This formatter must be available whenever the field type is available
+   *   (i.e. provided by the field type module, or by a module the field type
+   *   module depends on).
+   * @param bool $no_ui
+   *   (optional) A boolean stating that fields of this type cannot be created
+   *   through the UI.
+   * @param string|null $list_class
+   *   (optional) The typed data class used for wrapping multiple data items of
+   *   the type. Must implement the \Drupal\Core\TypedData\ListInterface.
+   * @param int|null $cardinality
+   *   (optional) An integer defining a fixed cardinality for this field type.
+   *   If this value is not set, cardinality can be configured in the field UI.
+   * @param array $constraints
+   *   (optional) An array of validation constraints for this type.
+   * @param array $config_dependencies
+   *   (optional) An array of configuration dependencies.
+   * @param array $column_groups
+   *   (optional) An array of column groups for the field type.
+   * @param array $serialized_property_names
+   *   (optional) An array of property names that should be serialized.
+   * @param string|null $deriver
+   *   (optional) The deriver class for the data type.
+   * @param string|null $module
+   *   The name of the module providing the field type plugin.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly TranslatableMarkup $label,
+    public readonly TranslatableMarkup|array|null $description = NULL,
+    public readonly string $category = '',
+    public readonly int $weight = 0,
+    public readonly ?string $default_widget = NULL,
+    public readonly ?string $default_formatter = NULL,
+    public readonly bool $no_ui = FALSE,
+    public readonly ?string $list_class = NULL,
+    public readonly ?int $cardinality = NULL,
+    public readonly array $constraints = [],
+    public readonly array $config_dependencies = [],
+    public readonly array $column_groups = [],
+    public readonly array $serialized_property_names = [],
+    public readonly ?string $deriver = NULL,
+    public readonly ?string $module = NULL,
+  ) {
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
index 228f3bb8d2c0..5bc4a206de16 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -45,7 +46,15 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl
    *   The field type category plugin manager.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, TypedDataManagerInterface $typed_data_manager, protected ?FieldTypeCategoryManagerInterface $fieldTypeCategoryManager = NULL) {
-    parent::__construct('Plugin/Field/FieldType', $namespaces, $module_handler, 'Drupal\Core\Field\FieldItemInterface', 'Drupal\Core\Field\Annotation\FieldType');
+    parent::__construct(
+      'Plugin/Field/FieldType',
+      $namespaces,
+      $module_handler,
+      FieldItemInterface::class,
+      FieldType::class,
+      'Drupal\Core\Field\Annotation\FieldType',
+    );
+
     $this->alterInfo('field_info');
     $this->setCacheBackend($cache_backend, 'field_types_plugins');
     $this->typedDataManager = $typed_data_manager;
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
index b7b03cfd7d4e..e52da2578574 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
@@ -2,26 +2,26 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Drupal\Core\TypedData\OptionsProviderInterface;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\OptionsProviderInterface;
 
 /**
  * Defines the 'boolean' entity field type.
- *
- * @FieldType(
- *   id = "boolean",
- *   label = @Translation("Boolean"),
- *   description = @Translation("Field to store a true or false value."),
- *   default_widget = "boolean_checkbox",
- *   default_formatter = "boolean",
- * )
  */
+#[FieldType(
+  id: "boolean",
+  label: new TranslatableMarkup("Boolean"),
+  description: new TranslatableMarkup("Field to store a true or false value."),
+  default_widget: "boolean_checkbox",
+  default_formatter: "boolean",
+)]
 class BooleanItem extends FieldItemBase implements OptionsProviderInterface {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php
index 9e737758ab3a..7116809acdb4 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php
@@ -2,24 +2,27 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\ChangedFieldItemList;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Defines the 'changed' entity field type.
  *
  * Based on a field of this type, entity types can easily implement the
  * EntityChangedInterface.
  *
- * @FieldType(
- *   id = "changed",
- *   label = @Translation("Last changed"),
- *   description = @Translation("An entity field containing a UNIX timestamp of when the entity has been last updated."),
- *   no_ui = TRUE,
- *   default_widget = "datetime_timestamp",
- *   default_formatter = "timestamp",
- *   list_class = "\Drupal\Core\Field\ChangedFieldItemList"
- * )
- *
  * @see \Drupal\Core\Entity\EntityChangedInterface
  */
+#[FieldType(
+  id: "changed",
+  label: new TranslatableMarkup("Last changed"),
+  description: new TranslatableMarkup("An entity field containing a UNIX timestamp of when the entity has been last updated."),
+  default_widget: "datetime_timestamp",
+  default_formatter: "timestamp",
+  no_ui: TRUE,
+  list_class: ChangedFieldItemList::class,
+)]
 class ChangedItem extends CreatedItem {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php
index 78a66a7f849a..94faaf446435 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Defines the 'created' entity field type.
- *
- * @FieldType(
- *   id = "created",
- *   label = @Translation("Created"),
- *   description = @Translation("An entity field containing a UNIX timestamp of when the entity has been created."),
- *   no_ui = TRUE,
- *   default_widget = "datetime_timestamp",
- *   default_formatter = "timestamp"
- * )
  */
+#[FieldType(
+  id: "created",
+  label: new TranslatableMarkup("Created"),
+  description: new TranslatableMarkup("An entity field containing a UNIX timestamp of when the entity has been created."),
+  default_widget: "datetime_timestamp",
+  default_formatter: "timestamp",
+  no_ui: TRUE,
+)]
 class CreatedItem extends TimestampItem {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
index 561a2d9151a6..6997c1bb8fa5 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
@@ -2,29 +2,29 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'decimal' field type.
- *
- * @FieldType(
- *   id = "decimal",
- *   label = @Translation("Number (decimal)"),
- *   description = {
- *     @Translation("Ideal for exact counts and measures (prices, temperatures, distances, volumes, etc.)"),
- *     @Translation("Stores a number in the database in a fixed decimal format"),
- *     @Translation("For example, 12.34 km or € when used for further detailed calculations (such as summing many of these)"),
- *   },
- *   category = "number",
- *   weight = -30,
- *   default_widget = "number",
- *   default_formatter = "number_decimal"
- * )
  */
+#[FieldType(
+  id: "decimal",
+  label: new TranslatableMarkup("Number (decimal)"),
+  description: [
+    new TranslatableMarkup("Ideal for exact counts and measures (prices, temperatures, distances, volumes, etc.)"),
+    new TranslatableMarkup("Stores a number in the database in a fixed decimal format"),
+    new TranslatableMarkup("For example, 12.34 km or € when used for further detailed calculations (such as summing many of these)"),
+  ],
+  category: "number",
+  weight: -30,
+  default_widget: "number",
+  default_formatter: "number_decimal"
+)]
 class DecimalItem extends NumericItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
index 639d8d5e2864..33c8849f3f95 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
@@ -12,15 +13,14 @@
 
 /**
  * Defines the 'email' field type.
- *
- * @FieldType(
- *   id = "email",
- *   label = @Translation("Email"),
- *   description = @Translation("Field to store an email address."),
- *   default_widget = "email_default",
- *   default_formatter = "basic_string"
- * )
  */
+#[FieldType(
+  id: "email",
+  label: new TranslatableMarkup("Email"),
+  description: new TranslatableMarkup("Field to store an email address."),
+  default_widget: "email_default",
+  default_formatter: "basic_string"
+)]
 class EmailItem extends FieldItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 782ecba82e9e..b2e621fd12d0 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -10,6 +10,8 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\EntityReferenceFieldItemList;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldException;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
@@ -29,17 +31,16 @@
  *
  * Supported settings (below the definition's 'settings' key) are:
  * - target_type: The entity type to reference. Required.
- *
- * @FieldType(
- *   id = "entity_reference",
- *   label = @Translation("Entity reference"),
- *   description = @Translation("An entity field containing an entity reference."),
- *   category = "reference",
- *   default_widget = "entity_reference_autocomplete",
- *   default_formatter = "entity_reference_label",
- *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
- * )
  */
+#[FieldType(
+  id: "entity_reference",
+  label: new TranslatableMarkup("Entity reference"),
+  description: new TranslatableMarkup("An entity field containing an entity reference."),
+  category: "reference",
+  default_widget: "entity_reference_autocomplete",
+  default_formatter: "entity_reference_label",
+  list_class: EntityReferenceFieldItemList::class,
+)]
 class EntityReferenceItem extends EntityReferenceItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
index 2e120c0ad1d4..8ae9fe36834d 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
@@ -2,28 +2,29 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'float' field type.
- *
- * @FieldType(
- *   id = "float",
- *   label = @Translation("Number (float)"),
- *   description = {
- *     @Translation("In most instances, it is best to use Number (decimal) instead, as decimal numbers stored as floats may contain errors in precision"),
- *     @Translation("This type of field offers faster processing and more compact storage, but the differences are typically negligible on modern sites"),
- *     @Translation("For example, 123.4 km when used in imprecise contexts such as a walking trail distance"),
- *   },
- *   category = "number",
- *   weight = -10,
- *   default_widget = "number",
- *   default_formatter = "number_decimal"
- * )
  */
+#[FieldType(
+  id: "float",
+  label: new TranslatableMarkup("Number (float)"),
+  description: [
+    new TranslatableMarkup("In most instances, it is best to use Number (decimal) instead, as decimal numbers stored as floats may contain errors in precision"),
+    new TranslatableMarkup("This type of field offers faster processing and more compact storage, but the differences are typically negligible on modern sites"),
+    new TranslatableMarkup("For example, 123.4 km when used in imprecise contexts such as a walking trail distance"),
+  ],
+  category: "number",
+  weight: -10,
+  default_widget: "number",
+  default_formatter: "number_decimal"
+)]
 class FloatItem extends NumericItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index dcb4275d9d8e..a3125b2ea531 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -2,27 +2,27 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'integer' field type.
- *
- * @FieldType(
- *   id = "integer",
- *   label = @Translation("Number (integer)"),
- *   description = {
- *     @Translation("Number without decimals"),
- *     @Translation("For example, 123"),
- *   },
- *   category = "number",
- *   weight = -50,
- *   default_widget = "number",
- *   default_formatter = "number_integer"
- * )
  */
+#[FieldType(
+  id: "integer",
+  label: new TranslatableMarkup("Number (integer)"),
+  description: [
+    new TranslatableMarkup("Number without decimals"),
+    new TranslatableMarkup("For example, 123"),
+  ],
+  category: "number",
+  weight: -50,
+  default_widget: "number",
+  default_formatter: "number_integer"
+)]
 class IntegerItem extends NumericItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
index 5ca1449f5263..aec1052d5174 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
@@ -2,34 +2,35 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\DataReferenceDefinition;
 use Drupal\Core\TypedData\OptionsProviderInterface;
 
 /**
  * Defines the 'language' entity field item.
- *
- * @FieldType(
- *   id = "language",
- *   label = @Translation("Language"),
- *   description = @Translation("An entity field referencing a language."),
- *   default_widget = "language_select",
- *   default_formatter = "language",
- *   no_ui = TRUE,
- *   constraints = {
- *     "ComplexData" = {
- *       "value" = {
- *         "Length" = {"max" = 12}
- *       }
- *     }
- *   }
- * )
  */
+#[FieldType(
+  id: "language",
+  label: new TranslatableMarkup("Language"),
+  description: new TranslatableMarkup("An entity field referencing a language."),
+  default_widget: "language_select",
+  default_formatter: "language",
+  no_ui: TRUE,
+  constraints: [
+    "ComplexData" => [
+      "value" => [
+        "Length" => ["max" => 12],
+      ],
+    ],
+  ]
+)]
 class LanguageItem extends FieldItemBase implements OptionsProviderInterface {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
index be4887aaa70f..9f7eaef8d82a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\MapFieldItemList;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'map' entity field type.
- *
- * @FieldType(
- *   id = "map",
- *   label = @Translation("Map"),
- *   description = @Translation("An entity field for storing a serialized array of values."),
- *   no_ui = TRUE,
- *   list_class = "\Drupal\Core\Field\MapFieldItemList",
- * )
  */
+#[FieldType(
+  id: "map",
+  label: new TranslatableMarkup("Map"),
+  description: new TranslatableMarkup("An entity field for storing a serialized array of values."),
+  no_ui: TRUE,
+  list_class: MapFieldItemList::class,
+)]
 class MapItem extends FieldItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php
index 1e5eca9e9405..a7c8460a7c48 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php
@@ -3,20 +3,20 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Core\Entity\EntityMalformedException;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'password' entity field type.
- *
- * @FieldType(
- *   id = "password",
- *   label = @Translation("Password"),
- *   description = @Translation("An entity field containing a password value."),
- *   no_ui = TRUE,
- * )
  */
+#[FieldType(
+  id: "password",
+  label: new TranslatableMarkup("Password"),
+  description: new TranslatableMarkup("An entity field containing a password value."),
+  no_ui: TRUE,
+)]
 class PasswordItem extends StringItem {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index 0df368c5a752..75163e6f2c8a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -3,27 +3,28 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'string' entity field type.
- *
- * @FieldType(
- *   id = "string",
- *   label = @Translation("Text (plain)"),
- *   description = {
- *     @Translation("Ideal for titles and names"),
- *     @Translation("Efficient storage for short text"),
- *     @Translation("Requires specifying a maximum length"),
- *     @Translation("Good for fields with known or predictable length"),
- *   },
- *   category = "plain_text",
- *   default_widget = "string_textfield",
- *   default_formatter = "string"
- * )
  */
+#[FieldType(
+  id: "string",
+  label: new TranslatableMarkup("Text (plain)"),
+  description: [
+    new TranslatableMarkup("Ideal for titles and names"),
+    new TranslatableMarkup("Efficient storage for short text"),
+    new TranslatableMarkup("Requires specifying a maximum length"),
+    new TranslatableMarkup("Good for fields with known or predictable length"),
+  ],
+  category: "plain_text",
+  default_widget: "string_textfield",
+  default_formatter: "string"
+)]
 class StringItem extends StringItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
index b98f76fbfb8a..aafdeb2c8e2a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
@@ -3,25 +3,26 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'string_long' field type.
- *
- * @FieldType(
- *   id = "string_long",
- *   label = @Translation("Text (plain, long)"),
- *   description = {
- *     @Translation("Ideal for longer texts, like body or description"),
- *     @Translation("Supports long text without specifying a maximum length"),
- *     @Translation("May use more storage and be slower for searching and sorting"),
- *   },
- *   category = "plain_text",
- *   default_widget = "string_textarea",
- *   default_formatter = "basic_string",
- * )
  */
+#[FieldType(
+  id: "string_long",
+  label: new TranslatableMarkup("Text (plain, long)"),
+  description: [
+    new TranslatableMarkup("Ideal for longer texts, like body or description"),
+    new TranslatableMarkup("Supports long text without specifying a maximum length"),
+    new TranslatableMarkup("May use more storage and be slower for searching and sorting"),
+  ],
+  category: "plain_text",
+  default_widget: "string_textarea",
+  default_formatter: "basic_string",
+)]
 class StringLongItem extends StringItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
index 44ae5a7f1880..79d7353894bd 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
@@ -2,37 +2,38 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'timestamp' entity field type.
- *
- * @FieldType(
- *   id = "timestamp",
- *   label = @Translation("Timestamp"),
- *   description = {
- *     @Translation("Ideal for using date and time calculations or comparisons"),
- *     @Translation("Date and time stored in the form of seconds since January 1, 1970 (UTC)"),
- *     @Translation("Compact and efficient for storage, sorting and calculations"),
- *   },
- *   category = "date_time",
- *   default_widget = "datetime_timestamp",
- *   default_formatter = "timestamp",
- *   constraints = {
- *     "ComplexData" = {
- *       "value" = {
- *         "Range" = {
- *           "min" = "-2147483648",
- *           "max" = "2147483648",
- *         }
- *       }
- *     }
- *   }
- * )
  */
+#[FieldType(
+  id: "timestamp",
+  label: new TranslatableMarkup("Timestamp"),
+  description: [
+    new TranslatableMarkup("Ideal for using date and time calculations or comparisons"),
+    new TranslatableMarkup("Date and time stored in the form of seconds since January 1, 1970 (UTC)"),
+    new TranslatableMarkup("Compact and efficient for storage, sorting and calculations"),
+  ],
+  category: "date_time",
+  default_widget: "datetime_timestamp",
+  default_formatter: "timestamp",
+  constraints: [
+    "ComplexData" => [
+      "value" => [
+        "Range" => [
+          "min" => "-2147483648",
+          "max" => "2147483648",
+        ],
+      ],
+    ],
+  ]
+)]
 class TimestampItem extends FieldItemBase {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index 6194244041e0..bc3ede8ba955 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -3,8 +3,10 @@
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
@@ -13,16 +15,15 @@
  * URIs are not length limited by RFC 2616, but we need to provide a sensible
  * default. There is a de-facto limit of 2000 characters in browsers and other
  * implementors, so we go with 2048.
- *
- * @FieldType(
- *   id = "uri",
- *   label = @Translation("URI"),
- *   description = @Translation("An entity field containing a URI."),
- *   no_ui = TRUE,
- *   default_formatter = "uri_link",
- *   default_widget = "uri",
- * )
  */
+#[FieldType(
+  id: "uri",
+  label: new TranslatableMarkup("URI"),
+  description: new TranslatableMarkup("An entity field containing a URI."),
+  default_widget: "uri",
+  default_formatter: "uri_link",
+  no_ui: TRUE,
+)]
 class UriItem extends StringItem {
 
   /**
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php
index a5f94010880b..c52ba1a39e3e 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php
@@ -2,22 +2,23 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'uuid' entity field type.
  *
  * The field uses a newly generated UUID as default value.
- *
- * @FieldType(
- *   id = "uuid",
- *   label = @Translation("UUID"),
- *   description = @Translation("An entity field containing a UUID."),
- *   no_ui = TRUE,
- *   default_formatter = "string"
- * )
  */
+#[FieldType(
+  id: "uuid",
+  label: new TranslatableMarkup("UUID"),
+  description: new TranslatableMarkup("An entity field containing a UUID."),
+  default_formatter: "string",
+  no_ui: TRUE
+)]
 class UuidItem extends StringItem {
 
   /**
diff --git a/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php b/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php
index 8833651a3dca..1f16bf7386f5 100644
--- a/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php
+++ b/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php
@@ -2,31 +2,32 @@
 
 namespace Drupal\comment\Plugin\Field\FieldType;
 
+use Drupal\comment\CommentFieldItemList;
 use Drupal\comment\CommentInterface;
 use Drupal\comment\CommentManagerInterface;
 use Drupal\comment\Entity\CommentType;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Session\AnonymousUserSession;
-use Drupal\Core\Url;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'comment' field type.
- *
- * @FieldType(
- *   id = "comment",
- *   label = @Translation("Comments"),
- *   description = @Translation("This field manages configuration and presentation of comments on an entity."),
- *   list_class = "\Drupal\comment\CommentFieldItemList",
- *   default_widget = "comment_default",
- *   default_formatter = "comment_default",
- *   cardinality = 1,
- * )
  */
+#[FieldType(
+  id: "comment",
+  label: new TranslatableMarkup("Comments"),
+  description: new TranslatableMarkup("This field manages configuration and presentation of comments on an entity."),
+  default_widget: "comment_default",
+  default_formatter: "comment_default",
+  list_class: CommentFieldItemList::class,
+  cardinality: 1,
+)]
 class CommentItem extends FieldItemBase implements CommentItemInterface {
 
   /**
diff --git a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
index ee104a410eda..68742eba0583 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
@@ -2,31 +2,31 @@
 
 namespace Drupal\datetime\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'datetime' field type.
- *
- * @FieldType(
- *   id = "datetime",
- *   label = @Translation("Date"),
- *   description = {
- *     @Translation("Ideal when date and time needs to be input by users, like event dates and times"),
- *     @Translation("Date or date and time stored in a readable string format"),
- *     @Translation("Easy to read and understand for humans"),
- *   },
- *   category = "date_time",
- *   default_widget = "datetime_default",
- *   default_formatter = "datetime_default",
- *   list_class = "\Drupal\datetime\Plugin\Field\FieldType\DateTimeFieldItemList",
- *   constraints = {"DateTimeFormat" = {}}
- * )
  */
+#[FieldType(
+  id: "datetime",
+  label: new TranslatableMarkup("Date"),
+  description: [
+    new TranslatableMarkup("Ideal when date and time needs to be input by users, like event dates and times"),
+    new TranslatableMarkup("Date or date and time stored in a readable string format"),
+    new TranslatableMarkup("Easy to read and understand for humans"),
+  ],
+  category: "date_time",
+  default_widget: "datetime_default",
+  default_formatter: "datetime_default",
+  list_class: DateTimeFieldItemList::class,
+  constraints: ["DateTimeFormat" => []]
+)]
 class DateTimeItem extends FieldItemBase implements DateTimeItemInterface {
 
   /**
diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php b/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
index 3020a98de6fa..85680e691463 100644
--- a/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
+++ b/core/modules/datetime_range/src/Plugin/Field/FieldType/DateRangeItem.php
@@ -2,9 +2,11 @@
 
 namespace Drupal\datetime_range\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\datetime\DateTimeComputed;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
@@ -12,21 +14,20 @@
 
 /**
  * Plugin implementation of the 'daterange' field type.
- *
- * @FieldType(
- *   id = "daterange",
- *   label = @Translation("Date range"),
- *   category = "date_time",
- *   description = {
- *     @Translation("Ideal for storing durations that consist of start and end dates (and times)"),
- *     @Translation("Choose between setting both date and time, or date only, for each duration"),
- *     @Translation("The system automatically validates that the end date (and time) is later than the start, and both fields are completed"),
- *   },
- *   default_widget = "daterange_default",
- *   default_formatter = "daterange_default",
- *   list_class = "\Drupal\datetime_range\Plugin\Field\FieldType\DateRangeFieldItemList"
- * )
  */
+#[FieldType(
+  id: "daterange",
+  label: new TranslatableMarkup("Date range"),
+  description: [
+    new TranslatableMarkup("Ideal for storing durations that consist of start and end dates (and times)"),
+    new TranslatableMarkup("Choose between setting both date and time, or date only, for each duration"),
+    new TranslatableMarkup("The system automatically validates that the end date (and time) is later than the start, and both fields are completed"),
+  ],
+  category: "date_time",
+  default_widget: "daterange_default",
+  default_formatter: "daterange_default",
+  list_class: DateRangeFieldItemList::class,
+)]
 class DateRangeItem extends DateTimeItem {
 
   /**
diff --git a/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php b/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
index 5c0c964a6bcb..5b82a66f6d08 100644
--- a/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
+++ b/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\editor_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\text\Plugin\Field\FieldType\TextLongItem;
 
 /**
  * Plugin implementation of the 'editor_test_text_long' field type.
- *
- * @FieldType(
- *   id = "editor_test_text_long",
- *   label = @Translation("Filter test text (formatted, long)"),
- *   description = @Translation("This field stores a long text with a text format."),
- *   default_widget = "text_textarea",
- *   default_formatter = "text_default"
- * )
  */
+#[FieldType(
+  id: "editor_test_text_long",
+  label: new TranslatableMarkup("Filter test text (formatted, long)"),
+  description: new TranslatableMarkup("This field stores a long text with a text format."),
+  default_widget: "text_textarea",
+  default_formatter: "text_default"
+)]
 class EditorTestTextLongItem extends TextLongItem {
 
 }
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 7656b41d58b7..f8d72a26dbac 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -20,8 +20,8 @@
  * and so on. The data type(s) accepted by a field are defined in the class
  * implementing \Drupal\Core\Field\FieldItemInterface::schema() method.
  *
- * Field types are plugins annotated with class
- * \Drupal\Core\Field\Annotation\FieldType, and implement plugin interface
+ * Field types are plugins with \Drupal\Core\Field\Attribute\FieldType
+ * attributes and implement plugin interface
  * \Drupal\Core\Field\FieldItemInterface. Field Type plugins are managed by the
  * \Drupal\Core\Field\FieldTypePluginManager class. Field type classes usually
  * extend base class \Drupal\Core\Field\FieldItemBase. Field-type plugins need
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/HiddenTestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/HiddenTestItem.php
index 3f90cc335389..6a5fb1e17832 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/HiddenTestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/HiddenTestItem.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Defines the 'hidden_test' entity field item.
- *
- * @FieldType(
- *   id = "hidden_test_field",
- *   label = @Translation("Hidden from UI test field"),
- *   description = @Translation("Dummy hidden field type used for tests."),
- *   no_ui = TRUE,
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default"
- * )
  */
+#[FieldType(
+  id: "hidden_test_field",
+  label: new TranslatableMarkup("Hidden from UI test field"),
+  description: new TranslatableMarkup("Dummy hidden field type used for tests."),
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default",
+  no_ui: TRUE
+)]
 class HiddenTestItem extends TestItem {
 
 }
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
index 2f64264b54dd..6605aabdb50a 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
@@ -2,22 +2,22 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 
 /**
  * Defines the 'test_field' entity field item.
- *
- * @FieldType(
- *   id = "test_field",
- *   label = @Translation("Test field"),
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default"
- * )
  */
+#[FieldType(
+  id: "test_field",
+  label: new TranslatableMarkup("Test field"),
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default"
+)]
 class TestItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithDependencies.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithDependencies.php
index 217d1abcefa3..3e1631f04a92 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithDependencies.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithDependencies.php
@@ -2,24 +2,23 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'test_field_with_dependencies' entity field item.
- *
- * @FieldType(
- *   id = "test_field_with_dependencies",
- *   label = @Translation("Test field with dependencies"),
- *   description = @Translation("Dummy field type used for tests."),
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default",
- *   config_dependencies = {
- *     "module" = {
- *       "system"
- *     }
- *   }
- * )
  */
+#[FieldType(
+  id: "test_field_with_dependencies",
+  label: new TranslatableMarkup("Test field with dependencies"),
+  description: new TranslatableMarkup("Dummy field type used for tests."),
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default",
+  config_dependencies: [
+    "module" => ["system"],
+  ]
+)]
 class TestItemWithDependencies extends TestItem {
 
   /**
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithMultipleDescriptions.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithMultipleDescriptions.php
index d193ebf2f811..0148e127865c 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithMultipleDescriptions.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithMultipleDescriptions.php
@@ -4,20 +4,22 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Defines the 'test_field_with_multiple_descriptions' entity field item.
- *
- * @FieldType(
- *   id = "test_field_with_multiple_descriptions",
- *   label = @Translation("Test field (multiple descriptions"),
- *   description = {
- *     @Translation("This multiple line description needs to use an array"),
- *     @Translation("This second line contains important information"),
- *   },
- *   category = "field_test_descriptions",
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default"
- * )
  */
+#[FieldType(
+  id: "test_field_with_multiple_descriptions",
+  label: new TranslatableMarkup("Test field (multiple descriptions"),
+  description: [
+    new TranslatableMarkup("This multiple line description needs to use an array"),
+    new TranslatableMarkup("This second line contains important information"),
+  ],
+  category: "field_test_descriptions",
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default"
+)]
 class TestItemWithMultipleDescriptions extends TestItem {
 }
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
index a35642ac134d..c9d76509feb0 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
@@ -2,21 +2,21 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'test_field_with_preconfigured_options' entity field item.
- *
- * @FieldType(
- *   id = "test_field_with_preconfigured_options",
- *   label = @Translation("Test field with preconfigured options"),
- *   description = @Translation("Dummy field type used for tests."),
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default"
- * )
  */
+#[FieldType(
+  id: "test_field_with_preconfigured_options",
+  label: new TranslatableMarkup("Test field with preconfigured options"),
+  description: new TranslatableMarkup("Dummy field type used for tests."),
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default"
+)]
 class TestItemWithPreconfiguredOptions extends TestItem implements PreconfiguredFieldUiOptionsInterface {
 
   /**
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithSingleDescription.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithSingleDescription.php
index e14dd039bb5c..6c323281833f 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithSingleDescription.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithSingleDescription.php
@@ -4,17 +4,19 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Defines the 'test_field_with_single_description' entity field item.
- *
- * @FieldType(
- *   id = "test_field_with_single_description",
- *   label = @Translation("Test field (single description"),
- *   description = @Translation("This one-line field description is important for testing"),
- *   category = "field_test_descriptions",
- *   default_widget = "test_field_widget",
- *   default_formatter = "field_test_default"
- * )
  */
+#[FieldType(
+  id: "test_field_with_single_description",
+  label: new TranslatableMarkup("Test field (single description"),
+  description: new TranslatableMarkup("This one-line field description is important for testing"),
+  category: "field_test_descriptions",
+  default_widget: "test_field_widget",
+  default_formatter: "field_test_default"
+)]
 class TestItemWithSingleDescription extends TestItem {
 }
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
index d5647c2f6a84..160b6bfc30f5 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestObjectItem.php
@@ -2,21 +2,22 @@
 
 namespace Drupal\field_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 
 /**
  * Defines the 'test_object_field' entity field item.
- *
- * @FieldType(
- *   id = "test_object_field",
- *   label = @Translation("Test object field"),
- *   description = @Translation("Test field type that has an object to test serialization"),
- *   default_widget = "test_object_field_widget",
- *   default_formatter = "object_field_test_default"
- * )
  */
+#[FieldType(
+  id: "test_object_field",
+  label: new TranslatableMarkup("Test object field"),
+  description: new TranslatableMarkup("Test field type that has an object to test serialization"),
+  default_widget: "test_object_field_widget",
+  default_formatter: "object_field_test_default"
+)]
 class TestObjectItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index 60bc20af7fe7..799d31af8ecb 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Utility\Environment;
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
@@ -20,21 +21,20 @@
 
 /**
  * Plugin implementation of the 'file' field type.
- *
- * @FieldType(
- *   id = "file",
- *   label = @Translation("File"),
- *   description = {
- *     @Translation("For uploading files"),
- *     @Translation("Can be configured with options such as allowed file extensions and maximum upload size"),
- *   },
- *   category = "file_upload",
- *   default_widget = "file_generic",
- *   default_formatter = "file_default",
- *   list_class = "\Drupal\file\Plugin\Field\FieldType\FileFieldItemList",
- *   constraints = {"ReferenceAccess" = {}, "FileValidation" = {}}
- * )
  */
+#[FieldType(
+  id: "file",
+  label: new TranslatableMarkup("File"),
+  description: [
+    new TranslatableMarkup("For uploading files"),
+    new TranslatableMarkup("Can be configured with options such as allowed file extensions and maximum upload size"),
+  ],
+  category: "file_upload",
+  default_widget: "file_generic",
+  default_formatter: "file_default",
+  list_class: FileFieldItemList::class,
+  constraints: ["ReferenceAccess" => [], "FileValidation" => []]
+)]
 class FileItem extends EntityReferenceItem {
 
   use FileValidatorSettingsTrait;
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
index 33bc15ef03f1..0e116b597a90 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
@@ -2,23 +2,24 @@
 
 namespace Drupal\file\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\UriItem;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\file\ComputedFileUrl;
 
 /**
  * File-specific plugin implementation of a URI item to provide a full URL.
- *
- * @FieldType(
- *   id = "file_uri",
- *   label = @Translation("File URI"),
- *   description = @Translation("An entity field containing a file URI, and a computed root-relative file URL."),
- *   no_ui = TRUE,
- *   default_formatter = "file_uri",
- *   default_widget = "uri",
- * )
  */
+#[FieldType(
+  id: "file_uri",
+  label: new TranslatableMarkup("File URI"),
+  description: new TranslatableMarkup("An entity field containing a file URI, and a computed root-relative file URL."),
+  default_widget: "uri",
+  default_formatter: "file_uri",
+  no_ui: TRUE,
+)]
 class FileUriItem extends UriItem {
 
   /**
diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
index d98f6c1534e4..29fa146d4874 100644
--- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Utility\Random;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\File\Exception\FileException;
@@ -14,43 +15,47 @@
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\file\Entity\File;
+use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
 use Drupal\file\Plugin\Field\FieldType\FileItem;
 
 /**
  * Plugin implementation of the 'image' field type.
- *
- * @FieldType(
- *   id = "image",
- *   label = @Translation("Image"),
- *   description = {
- *     @Translation("For uploading images"),
- *     @Translation("Allows a user to upload an image with configurable extensions, image dimensions, upload size"),
- *     @Translation("Can be configured with options such as allowed file extensions, maximum upload size and image dimensions minimums/maximums"),
- *   },
- *   category = "file_upload",
- *   default_widget = "image_image",
- *   default_formatter = "image",
- *   column_groups = {
- *     "file" = {
- *       "label" = @Translation("File"),
- *       "columns" = {
- *         "target_id", "width", "height"
- *       },
- *       "require_all_groups_for_translation" = TRUE
- *     },
- *     "alt" = {
- *       "label" = @Translation("Alt"),
- *       "translatable" = TRUE
- *     },
- *     "title" = {
- *       "label" = @Translation("Title"),
- *       "translatable" = TRUE
- *     },
- *   },
- *   list_class = "\Drupal\file\Plugin\Field\FieldType\FileFieldItemList",
- *   constraints = {"ReferenceAccess" = {}, "FileValidation" = {}}
- * )
  */
+#[FieldType(
+  id: "image",
+  label: new TranslatableMarkup("Image"),
+  description: [
+    new TranslatableMarkup("For uploading images"),
+    new TranslatableMarkup("Allows a user to upload an image with configurable extensions, image dimensions, upload size"),
+    new TranslatableMarkup(
+      "Can be configured with options such as allowed file extensions, maximum upload size and image dimensions minimums/maximums"
+    ),
+  ],
+  category: "file_upload",
+  default_widget: "image_image",
+  default_formatter: "image",
+  list_class: FileFieldItemList::class,
+  constraints: ["ReferenceAccess" => [], "FileValidation" => []],
+  column_groups: [
+    "file" => [
+      "label" => new TranslatableMarkup("File"),
+      "columns" => [
+        "target_id",
+        "width",
+        "height",
+      ],
+      "require_all_groups_for_translation" => TRUE,
+    ],
+    "alt" => [
+      "label" => new TranslatableMarkup("Alt"),
+      "translatable" => TRUE,
+    ],
+    "title" => [
+      "label" => new TranslatableMarkup("Title"),
+      "translatable" => TRUE,
+    ],
+  ]
+)]
 class ImageItem extends FileItem {
 
   use LoggerChannelTrait;
diff --git a/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php b/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
index 587661234564..bf2453055c25 100644
--- a/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
+++ b/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
@@ -2,22 +2,22 @@
 
 namespace Drupal\image_module_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines a dummy field containing an AJAX handler.
- *
- * @FieldType(
- *   id = "image_module_test_dummy_ajax",
- *   label = @Translation("Dummy AJAX"),
- *   description = @Translation("A field containing an AJAX handler."),
- *   default_widget = "image_module_test_dummy_ajax_widget",
- *   default_formatter = "image_module_test_dummy_ajax_formatter"
- * )
  */
+#[FieldType(
+  id: "image_module_test_dummy_ajax",
+  label: new TranslatableMarkup("Dummy AJAX"),
+  description: new TranslatableMarkup("A field containing an AJAX handler."),
+  default_widget: "image_module_test_dummy_ajax_widget",
+  default_formatter: "image_module_test_dummy_ajax_formatter"
+)]
 class DummyAjaxItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
index a132c8234cb2..6710171752c1 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
@@ -2,11 +2,13 @@
 
 namespace Drupal\layout_builder\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\layout_builder\Field\LayoutSectionItemList;
 use Drupal\layout_builder\Section;
 
 /**
@@ -15,17 +17,16 @@
  * @internal
  *   Plugin classes are internal.
  *
- * @FieldType(
- *   id = "layout_section",
- *   label = @Translation("Layout Section"),
- *   description = @Translation("Layout Section"),
- *   list_class = "\Drupal\layout_builder\Field\LayoutSectionItemList",
- *   no_ui = TRUE,
- *   cardinality = \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
- * )
- *
  * @property \Drupal\layout_builder\Section $section
  */
+#[FieldType(
+  id: "layout_section",
+  label: new TranslatableMarkup("Layout Section"),
+  description: new TranslatableMarkup("Layout Section"),
+  no_ui: TRUE,
+  list_class: LayoutSectionItemList::class,
+  cardinality: FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
+)]
 class LayoutSectionItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
index b77acedf7ff2..2849b0ac83e9 100644
--- a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
@@ -3,28 +3,33 @@
 namespace Drupal\link\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\MapDataDefinition;
 use Drupal\Core\Url;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\link\LinkItemInterface;
 
 /**
  * Plugin implementation of the 'link' field type.
- *
- * @FieldType(
- *   id = "link",
- *   label = @Translation("Link"),
- *   description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."),
- *   default_widget = "link_default",
- *   default_formatter = "link",
- *   constraints = {"LinkType" = {}, "LinkAccess" = {}, "LinkExternalProtocols" = {}, "LinkNotExistingInternal" = {}}
- * )
  */
+#[FieldType(
+  id: "link",
+  label: new TranslatableMarkup("Link"),
+  description: new TranslatableMarkup("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."),
+  default_widget: "link_default",
+  default_formatter: "link",
+  constraints: [
+    "LinkType" => [],
+    "LinkAccess" => [],
+    "LinkExternalProtocols" => [],
+    "LinkNotExistingInternal" => [],
+  ]
+)]
 class LinkItem extends FieldItemBase implements LinkItemInterface {
 
   /**
diff --git a/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php b/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
index 9fd286d8af18..ff0c8f00640d 100644
--- a/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
+++ b/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\media_library_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\EntityReferenceFieldItemList;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Plugin implementation of the 'entity_reference_subclass' field type.
- *
- * @FieldType(
- *   id = "entity_reference_subclass",
- *   label = @Translation("Entity reference subclass"),
- *   description = @Translation("An entity field containing an entity reference."),
- *   category = "reference",
- *   default_widget = "entity_reference_autocomplete",
- *   default_formatter = "entity_reference_label",
- *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
- * )
  */
+#[FieldType(
+  id: "entity_reference_subclass",
+  label: new TranslatableMarkup("Entity reference subclass"),
+  description: new TranslatableMarkup("An entity field containing an entity reference."),
+  category: "reference",
+  default_widget: "entity_reference_autocomplete",
+  default_formatter: "entity_reference_label",
+  list_class: EntityReferenceFieldItemList::class,
+)]
 class EntityReferenceItemSubclass extends EntityReferenceItem {
 }
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
index d2539dd66d18..ec213b08876c 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\options\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldFilteredMarkup;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -11,20 +12,19 @@
 
 /**
  * Plugin implementation of the 'list_float' field type.
- *
- * @FieldType(
- *   id = "list_float",
- *   label = @Translation("List (float)"),
- *   description = {
- *     @Translation("Values stored are floating-point numbers"),
- *     @Translation("For example, 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1"),
- *   },
- *   category = "selection_list",
- *   weight = -10,
- *   default_widget = "options_select",
- *   default_formatter = "list_default",
- * )
  */
+#[FieldType(
+  id: "list_float",
+  label: new TranslatableMarkup("List (float)"),
+  description: [
+    new TranslatableMarkup("Values stored are floating-point numbers"),
+    new TranslatableMarkup("For example, 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1"),
+  ],
+  category: "selection_list",
+  weight: -10,
+  default_widget: "options_select",
+  default_formatter: "list_default",
+)]
 class ListFloatItem extends ListItemBase {
 
   /**
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
index 5132bb6c6574..f053b101039c 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\options\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldFilteredMarkup;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -11,20 +12,19 @@
 
 /**
  * Plugin implementation of the 'list_integer' field type.
- *
- * @FieldType(
- *   id = "list_integer",
- *   label = @Translation("List (integer)"),
- *   description = {
- *     @Translation("Values stored are numbers without decimals"),
- *     @Translation("For example, 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month"),
- *   },
- *   category = "selection_list",
- *   weight = -30,
- *   default_widget = "options_select",
- *   default_formatter = "list_default",
- * )
  */
+#[FieldType(
+  id: "list_integer",
+  label: new TranslatableMarkup("List (integer)"),
+  description: [
+    new TranslatableMarkup("Values stored are numbers without decimals"),
+    new TranslatableMarkup("For example, 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month"),
+  ],
+  category: "selection_list",
+  weight: -30,
+  default_widget: "options_select",
+  default_formatter: "list_default",
+)]
 class ListIntegerItem extends ListItemBase {
 
   /**
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
index 22cd0d9ac678..332a1768c23e 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\options\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldFilteredMarkup;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -11,20 +12,19 @@
 
 /**
  * Plugin implementation of the 'list_string' field type.
- *
- * @FieldType(
- *   id = "list_string",
- *   label = @Translation("List (text)"),
- *   description = {
- *     @Translation("Values stored are text values"),
- *     @Translation("For example, 'US States': IL => Illinois, IA => Iowa, IN => Indiana"),
- *   },
- *   category = "selection_list",
- *   weight = -50,
- *   default_widget = "options_select",
- *   default_formatter = "list_default",
- * )
  */
+#[FieldType(
+  id: "list_string",
+  label: new TranslatableMarkup("List (text)"),
+  description: [
+    new TranslatableMarkup("Values stored are text values"),
+    new TranslatableMarkup("For example, 'US States': IL => Illinois, IA => Iowa, IN => Indiana"),
+  ],
+  category: "selection_list",
+  weight: -50,
+  default_widget: "options_select",
+  default_formatter: "list_default",
+)]
 class ListStringItem extends ListItemBase {
 
   /**
diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
index ba57f2467c95..6dd9111b225d 100644
--- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
+++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
@@ -3,24 +3,25 @@
 namespace Drupal\path\Plugin\Field\FieldType;
 
 use Drupal\Component\Utility\Random;
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Defines the 'path' entity field type.
- *
- * @FieldType(
- *   id = "path",
- *   label = @Translation("Path"),
- *   description = @Translation("An entity field containing a path alias and related data."),
- *   no_ui = TRUE,
- *   default_widget = "path",
- *   list_class = "\Drupal\path\Plugin\Field\FieldType\PathFieldItemList",
- *   constraints = {"PathAlias" = {}},
- * )
  */
+#[FieldType(
+  id: "path",
+  label: new TranslatableMarkup("Path"),
+  description: new TranslatableMarkup("An entity field containing a path alias and related data."),
+  default_widget: "path",
+  no_ui: TRUE,
+  list_class: PathFieldItemList::class,
+  constraints: ["PathAlias" => []],
+)]
 class PathItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
index f0d6beacefa6..de5a8f12f652 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
@@ -2,18 +2,19 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'field_method_invocation_order_test' entity field type.
- *
- * @FieldType(
- *   id = "auto_incrementing_test",
- *   label = @Translation("Auto incrementing test field item"),
- *   description = @Translation("An entity field designed to test the field method invocation order."),
- *   no_ui = TRUE,
- * )
  */
+#[FieldType(
+  id: "auto_incrementing_test",
+  label: new TranslatableMarkup("Auto incrementing test field item"),
+  description: new TranslatableMarkup("An entity field designed to test the field method invocation order."),
+  no_ui: TRUE,
+)]
 class AutoIncrementingTestItem extends IntegerItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ChangedTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ChangedTestItem.php
index 66b5b57f487e..3a5495ab76e7 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ChangedTestItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ChangedTestItem.php
@@ -2,23 +2,25 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\ChangedFieldItemList;
 use Drupal\Core\Field\Plugin\Field\FieldType\ChangedItem;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'changed_test' entity field type.
  *
  * Wraps Drupal\Core\Field\Plugin\Field\FieldType\ChangedItem.
  *
- * @FieldType(
- *   id = "changed_test",
- *   label = @Translation("Last changed"),
- *   description = @Translation("An entity field containing a UNIX timestamp of when the entity has been last updated."),
- *   no_ui = TRUE,
- *   list_class = "\Drupal\Core\Field\ChangedFieldItemList"
- * )
- *
  * @see \Drupal\Core\Entity\EntityChangedInterface
  */
+#[FieldType(
+  id: "changed_test",
+  label: new TranslatableMarkup("Last changed"),
+  description: new TranslatableMarkup("An entity field containing a UNIX timestamp of when the entity has been last updated."),
+  no_ui: TRUE,
+  list_class: ChangedFieldItemList::class,
+)]
 class ChangedTestItem extends ChangedItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
index 2ee17c143a8d..c0bc5e3f80d8 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -9,16 +10,15 @@
 
 /**
  * Defines the 'string' entity field type with cacheability metadata.
- *
- * @FieldType(
- *   id = "computed_test_cacheable_string_item",
- *   label = @Translation("Test Text (plain string with cacheability)"),
- *   description = @Translation("A test field containing a plain string value and cacheability metadata."),
- *   no_ui = TRUE,
- *   default_widget = "string_textfield",
- *   default_formatter = "string"
- * )
  */
+#[FieldType(
+  id: "computed_test_cacheable_string_item",
+  label: new TranslatableMarkup("Test Text (plain string with cacheability)"),
+  description: new TranslatableMarkup("A test field containing a plain string value and cacheability metadata."),
+  default_widget: "string_textfield",
+  default_formatter: "string",
+  no_ui: TRUE
+)]
 class ComputedTestCacheableStringItem extends StringItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
index 43e43c1a8220..dd2c0497e265 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -11,13 +12,12 @@
 
 /**
  * Defines the 'field_test' entity field type.
- *
- * @FieldType(
- *   id = "field_test",
- *   label = @Translation("Test field item"),
- *   description = @Translation("A field containing a plain string value."),
- * )
  */
+#[FieldType(
+  id: "field_test",
+  label: new TranslatableMarkup("Test field item"),
+  description: new TranslatableMarkup("A field containing a plain string value."),
+)]
 class FieldTestItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
index 647598d98810..9af51d7f50ba 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -10,15 +11,14 @@
 
 /**
  * Defines the 'Internal Property' entity test field type.
- *
- * @FieldType(
- *   id = "internal_property_test",
- *   label = @Translation("Internal Property (test)"),
- *   description = @Translation("A field containing one string, from which two strings are computed (one internal, one not)."),
- *   default_widget = "string_textfield",
- *   default_formatter = "string"
- * )
  */
+#[FieldType(
+  id: "internal_property_test",
+  label: new TranslatableMarkup("Internal Property (test)"),
+  description: new TranslatableMarkup("A field containing one string, from which two strings are computed (one internal, one not)."),
+  default_widget: "string_textfield",
+  default_formatter: "string"
+)]
 class InternalPropertyTestFieldItem extends StringItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
index f9606918f5c8..b3c6e05ffd14 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -9,13 +10,12 @@
 
 /**
  * Defines the 'serialized_item' entity field type.
- *
- * @FieldType(
- *   id = "serialized_item_test",
- *   label = @Translation("Test serialized field item"),
- *   description = @Translation("A field containing a serialized string value."),
- * )
  */
+#[FieldType(
+  id: "serialized_item_test",
+  label: new TranslatableMarkup("Test serialized field item"),
+  description: new TranslatableMarkup("A field containing a serialized string value."),
+)]
 class SerializedItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
index 9f5a674c5a09..3a49646fa40c 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -9,16 +10,13 @@
 
 /**
  * Defines the 'serialized_property_item_test' entity field type.
- *
- * @FieldType(
- *   id = "serialized_property_item_test",
- *   label = @Translation("Test serialized property field item"),
- *   description = @Translation("A field containing a string representing serialized data."),
- *   serialized_property_names = {
- *     "value"
- *   }
- * )
  */
+#[FieldType(
+  id: "serialized_property_item_test",
+  label: new TranslatableMarkup("Test serialized property field item"),
+  description: new TranslatableMarkup("A field containing a string representing serialized data."),
+  serialized_property_names: ["value"]
+)]
 class SerializedPropertyItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItem.php
index 4921dcf481ea..3d17c6ed5146 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItem.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 
 /**
  * Defines the 'shape' field type.
- *
- * @FieldType(
- *   id = "shape",
- *   label = @Translation("Shape"),
- *   description = @Translation("Another dummy field type."),
- * )
  */
+#[FieldType(
+  id: "shape",
+  label: new TranslatableMarkup("Shape"),
+  description: new TranslatableMarkup("Another dummy field type."),
+)]
 class ShapeItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItemRequired.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItemRequired.php
index fb3876094f55..7efd1ad70f2e 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItemRequired.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ShapeItemRequired.php
@@ -2,17 +2,18 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines the 'shape_required' field type.
- *
- * @FieldType(
- *   id = "shape_required",
- *   label = @Translation("Shape (required)"),
- *   description = @Translation("Yet another dummy field type."),
- * )
  */
+#[FieldType(
+  id: "shape_required",
+  label: new TranslatableMarkup("Shape (required)"),
+  description: new TranslatableMarkup("Yet another dummy field type."),
+)]
 class ShapeItemRequired extends ShapeItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
index 8d3d25e1f4bd..f28ba3543fb0 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entity_test\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -16,15 +17,14 @@
  * property name and one internal value are flattened.
  *
  * @see \Drupal\entity_test\Plugin\Field\FieldType\InternalPropertyTestFieldItem
- *
- * @FieldType(
- *   id = "single_internal_property_test",
- *   label = @Translation("Single Internal Property (test)"),
- *   description = @Translation("A field containing one string, from which one internal string is computed."),
- *   default_widget = "string_textfield",
- *   default_formatter = "string"
- * )
  */
+#[FieldType(
+  id: "single_internal_property_test",
+  label: new TranslatableMarkup("Single Internal Property (test)"),
+  description: new TranslatableMarkup("A field containing one string, from which one internal string is computed."),
+  default_widget: "string_textfield",
+  default_formatter: "string",
+)]
 class SingleInternalPropertyTestFieldItem extends StringItem {
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test_update/src/Plugin/Field/FieldType/MultiValueTestItem.php b/core/modules/system/tests/modules/entity_test_update/src/Plugin/Field/FieldType/MultiValueTestItem.php
index cb0881809a72..76fdb0f8e156 100644
--- a/core/modules/system/tests/modules/entity_test_update/src/Plugin/Field/FieldType/MultiValueTestItem.php
+++ b/core/modules/system/tests/modules/entity_test_update/src/Plugin/Field/FieldType/MultiValueTestItem.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\entity_test_update\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
+use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\Field\FieldItemBase;
 
 /**
  * Defines the 'multi_value_test' field type.
- *
- * @FieldType(
- *   id = "multi_value_test",
- *   label = @Translation("Multiple values test"),
- *   description = @Translation("Another dummy field type."),
- * )
  */
+#[FieldType(
+  id: "multi_value_test",
+  label: new TranslatableMarkup("Multiple values test"),
+  description: new TranslatableMarkup("Another dummy field type."),
+)]
 class MultiValueTestItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
index d0f0c17f05bf..dd97b961d14d 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
@@ -2,23 +2,23 @@
 
 namespace Drupal\telephone\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'telephone' field type.
- *
- * @FieldType(
- *   id = "telephone",
- *   label = @Translation("Telephone number"),
- *   description = @Translation("This field stores a telephone number."),
- *   default_widget = "telephone_default",
- *   default_formatter = "basic_string"
- * )
  */
+#[FieldType(
+  id: "telephone",
+  label: new TranslatableMarkup("Telephone number"),
+  description: new TranslatableMarkup("This field stores a telephone number."),
+  default_widget: "telephone_default",
+  default_formatter: "basic_string"
+)]
 class TelephoneItem extends FieldItemBase {
 
   /**
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
index 5492544db25b..5d3ac034b346 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
@@ -2,27 +2,28 @@
 
 namespace Drupal\text\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Plugin implementation of the 'text' field type.
- *
- * @FieldType(
- *   id = "text",
- *   label = @Translation("Text (formatted)"),
- *   description = {
- *     @Translation("Ideal for titles and names that need to support markup such as bold, italics or links"),
- *     @Translation("Efficient storage for short text"),
- *     @Translation("Requires specifying a maximum length"),
- *     @Translation("Good for fields with known or predictable lengths"),
- *   },
- *   category = "formatted_text",
- *   default_widget = "text_textfield",
- *   default_formatter = "text_default",
- *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
- * )
  */
+#[FieldType(
+  id: "text",
+  label: new TranslatableMarkup("Text (formatted)"),
+  description: [
+    new TranslatableMarkup("Ideal for titles and names that need to support markup such as bold, italics or links"),
+    new TranslatableMarkup("Efficient storage for short text"),
+    new TranslatableMarkup("Requires specifying a maximum length"),
+    new TranslatableMarkup("Good for fields with known or predictable lengths"),
+  ],
+  category: "formatted_text",
+  default_widget: "text_textfield",
+  default_formatter: "text_default",
+  list_class: TextFieldItemList::class,
+)]
 class TextItem extends TextItemBase {
 
   /**
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
index 3656d05e4fb5..52d29e11d9f9 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
@@ -2,25 +2,26 @@
 
 namespace Drupal\text\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Plugin implementation of the 'text_long' field type.
- *
- * @FieldType(
- *   id = "text_long",
- *   label = @Translation("Text (formatted, long)"),
- *   description = {
- *     @Translation("Ideal for longer texts, like body or description without a summary"),
- *     @Translation("Supports long text without specifying a maximum length"),
- *     @Translation("May use more storage and be slower for searching and sorting"),
- *   },
- *   category = "formatted_text",
- *   default_widget = "text_textarea",
- *   default_formatter = "text_default",
- *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
- * )
  */
+#[FieldType(
+  id: "text_long",
+  label: new TranslatableMarkup("Text (formatted, long)"),
+  description: [
+    new TranslatableMarkup("Ideal for longer texts, like body or description without a summary"),
+    new TranslatableMarkup("Supports long text without specifying a maximum length"),
+    new TranslatableMarkup("May use more storage and be slower for searching and sorting"),
+  ],
+  category: "formatted_text",
+  default_widget: "text_textarea",
+  default_formatter: "text_default",
+  list_class: TextFieldItemList::class,
+)]
 class TextLongItem extends TextItemBase {
 
   /**
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
index 9d43920520cf..f099a53a93c2 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
@@ -2,29 +2,29 @@
 
 namespace Drupal\text\Plugin\Field\FieldType;
 
+use Drupal\Core\Field\Attribute\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
 
 /**
  * Plugin implementation of the 'text_with_summary' field type.
- *
- * @FieldType(
- *   id = "text_with_summary",
- *   label = @Translation("Text (formatted, long, with summary)"),
- *   description = {
- *     @Translation("Ideal for longer texts, like body or description with a summary"),
- *     @Translation("Allows specifying a summary for the text"),
- *     @Translation("Supports long text without specifying a maximum length"),
- *     @Translation("May use more storage and be slower for searching and sorting"),
- *   },
- *   category = "formatted_text",
- *   default_widget = "text_textarea_with_summary",
- *   default_formatter = "text_default",
- *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
- * )
  */
+#[FieldType(
+  id: "text_with_summary",
+  label: new TranslatableMarkup("Text (formatted, long, with summary)"),
+  description: [
+    new TranslatableMarkup("Ideal for longer texts, like body or description with a summary"),
+    new TranslatableMarkup("Allows specifying a summary for the text"),
+    new TranslatableMarkup("Supports long text without specifying a maximum length"),
+    new TranslatableMarkup("May use more storage and be slower for searching and sorting"),
+  ],
+  category: "formatted_text",
+  default_widget: "text_textarea_with_summary",
+  default_formatter: "text_default",
+  list_class: TextFieldItemList::class,
+)]
 class TextWithSummaryItem extends TextItemBase {
 
   /**
-- 
GitLab