diff --git a/PLUGIN_TYPES.md b/PLUGIN_TYPES.md
index aca9831..991f5cc 100644
--- a/PLUGIN_TYPES.md
+++ b/PLUGIN_TYPES.md
@@ -19,6 +19,11 @@ on the class, but the default class takes the following:
 - operations_provider_class (optional): the fully qualified name of a class that
   implements `\Drupal\plugin\PluginType\PluginTypeOperationsProviderInterface`. 
   Defaults to `\Drupal\plugin\PluginType\DefaultPluginTypeOperationsProvider`.
+- plugin_configuration_schema_id (optional): the ID of the plugin's 
+  configuration schema. Two following replacement tokens are supported:
+  - [plugin_type_id]: The ID of the plugin's type.
+  - [plugin_id]: the plugin's ID.
+  Defaults to "plugin.plugin_configuration.[plugin_type_id].[plugin_id]"
 
 A configuration schema MAY be provided for all configurable plugin types. If a 
 schema is provided, its name MUST be like
diff --git a/config/schema/plugin.schema.yml b/config/schema/plugin.schema.yml
index 087f2c8..5af9b9a 100644
--- a/config/schema/plugin.schema.yml
+++ b/config/schema/plugin.schema.yml
@@ -28,13 +28,13 @@ plugin.plugin_configuration.*:
 "field.value.plugin:*":
   label: Plugin collection field value
   mapping:
-    plugin_type_id:
-      label: Plugin type ID
-      type: string
     plugin_id:
       label: Plugin ID
       type: string
     plugin_configuration:
       label: Plugin configuration
-      type: plugin.plugin_configuration.[%parent.plugin_type_id].[%parent.plugin_id]:
+      type: "[%parent.plugin_configuration_schema_id]"
+    plugin_configuration_schema_id:
+      label: Plugin configuration
+      type: string
   type: config_object
diff --git a/plugin.plugin_type.yml b/plugin.plugin_type.yml
index 18c1c02..999dec5 100644
--- a/plugin.plugin_type.yml
+++ b/plugin.plugin_type.yml
@@ -9,11 +9,13 @@ block:
   provider: block
   plugin_manager_service_id: plugin.manager.block
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\BlockPluginDefinitionDecorator
+  plugin_configuration_schema_id: block.settings.[plugin_id]
 entity_reference_selector:
   label: Entity reference selector
   provider: core
   plugin_manager_service_id: plugin.manager.entity_reference_selection
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: entity_reference_selection.[plugin_id]
 field_type:
   label: Field type/item
   provider: core
@@ -151,6 +153,7 @@ migrate_source:
   provider: migrate
   plugin_manager_service_id: plugin.manager.migrate.source
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: migrate.source.[plugin_id]
 migrate_process:
   label: Migration process
   provider: migrate
@@ -161,6 +164,7 @@ migrate_destination:
   provider: migrate
   plugin_manager_service_id: plugin.manager.migrate.destination
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: migrate.destination.[plugin_id]
 migrate_id_map:
   label: Migration ID map
   provider: migrate
@@ -213,11 +217,13 @@ views_area:
   provider: views
   plugin_manager_service_id: plugin.manager.views.area
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.area.[plugin_id]
 views_argument:
   label: Views argument
   provider: views
   plugin_manager_service_id: plugin.manager.views.argument
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.argument.[plugin_id]
 views_argument_default:
   label: Views argument default
   provider: views
@@ -253,11 +259,13 @@ views_field:
   provider: views
   plugin_manager_service_id: plugin.manager.views.field
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.field.[plugin_id]
 views_filter:
   label: Views filter
   provider: views
   plugin_manager_service_id: plugin.manager.views.filter
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.filter.[plugin_id]
 views_join:
   label: Views join
   provider: views
@@ -278,6 +286,7 @@ views_relationship:
   provider: views
   plugin_manager_service_id: plugin.manager.views.relationship
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.relationship.[plugin_id]
 views_row:
   label: Views row
   provider: views
@@ -288,6 +297,7 @@ views_sort:
   provider: views
   plugin_manager_service_id: plugin.manager.views.sort
   plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
+  plugin_configuration_schema_id: views.sort.[plugin_id]
 views_style:
   label: Views style
   provider: views
diff --git a/src/Plugin/Field/FieldType/PluginCollectionItemBase.php b/src/Plugin/Field/FieldType/PluginCollectionItemBase.php
index 779853a..8a3f00d 100644
--- a/src/Plugin/Field/FieldType/PluginCollectionItemBase.php
+++ b/src/Plugin/Field/FieldType/PluginCollectionItemBase.php
@@ -12,9 +12,7 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\MapDataDefinition;
-use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
  * Provides a base for plugin collection field items.
@@ -24,14 +22,6 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC
   /**
    * {@inheritdoc}
    */
-  public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
-    parent::__construct($definition, $name, $parent);
-    $this->get('plugin_type_id')->setValue($this->getPluginType()->getId());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function __get($name) {
     // @todo Remove this override once https://www.drupal.org/node/2413471 has
     //   been fixed.
@@ -129,9 +119,6 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC
    * {@inheritdoc}
    */
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
-    $properties['plugin_type_id'] = DataDefinition::create('string')
-      ->setLabel(t('Plugin type ID'))
-      ->setReadOnly(TRUE);
     $properties['plugin_id'] = DataDefinition::create('plugin_id')
       ->setLabel(t('Plugin ID'));
     $properties['plugin_configuration'] = MapDataDefinition::create('plugin_configuration')
@@ -192,9 +179,9 @@ abstract class PluginCollectionItemBase extends FieldItemBase implements PluginC
     // ID and configuration. parent::getValue() skips computed properties, so we
     // must return them here.
     return [
-      'plugin_type_id' => $this->get('plugin_type_id')->getValue(),
       'plugin_id' => $this->get('plugin_id')->getValue(),
       'plugin_configuration' => $this->get('plugin_configuration')->getValue(),
+      'plugin_configuration_schema_id' => $this->getPluginType()->getPluginConfigurationSchemaId($this->get('plugin_id')->getValue()),
     ];
   }
 
diff --git a/src/PluginType/ConfigurablePluginTypeInterface.php b/src/PluginType/ConfigurablePluginTypeInterface.php
new file mode 100644
index 0000000..20718ed
--- /dev/null
+++ b/src/PluginType/ConfigurablePluginTypeInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin\PluginType\ConfigurablePluginTypeInterface.
+ */
+
+namespace Drupal\plugin\PluginType;
+
+/**
+ * Defines a plugin type of which the plugins are configurable.
+ */
+interface ConfigurablePluginTypeInterface extends PluginTypeInterface {
+
+  /**
+   * Gets the ID of the configuration schema for a plugin ID.
+   *
+   * @param string $plugin_id
+   *   The ID of the plugin for whose configuration to get the schema ID.
+   *
+   * @return string
+   */
+  public function getPluginConfigurationSchemaId($plugin_id);
+
+}
diff --git a/src/PluginType/PluginType.php b/src/PluginType/PluginType.php
index caf753c..f290e54 100644
--- a/src/PluginType/PluginType.php
+++ b/src/PluginType/PluginType.php
@@ -8,6 +8,7 @@
 namespace Drupal\plugin\PluginType;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -19,11 +20,23 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 /**
  * Provides a plugin type.
  */
-class PluginType implements PluginTypeInterface {
+class PluginType implements ConfigurablePluginTypeInterface {
 
   use DependencySerializationTrait;
 
   /**
+   * The ID of the configuration schema of plugins of this type.
+   *
+   * @var string
+   *   A configuration schema ID. It may contain the tokens "[plugin_type_id|
+   *   and "[plugin_id]", which will be replaced by the plugin type ID and
+   *   plugin ID respectively.
+   *
+   * @see self::getPluginConfigurationSchemaId()
+   */
+  protected $configurationSchemaId = 'plugin.plugin_configuration.[plugin_type_id].[plugin_id]';
+
+  /**
    * The ID.
    *
    * @var string
@@ -97,10 +110,12 @@ class PluginType implements PluginTypeInterface {
    *   The class resolver.
    * @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
    *   The plugin type's plugin manager.
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
+   *   The typed configuration manager.
    *
    * @param mixed[] $definition
    */
-  public function __construct(array $definition, TranslationInterface $string_translation, ClassResolverInterface $class_resolver, PluginManagerInterface $plugin_manager) {
+  public function __construct(array $definition, TranslationInterface $string_translation, ClassResolverInterface $class_resolver, PluginManagerInterface $plugin_manager, TypedConfigManagerInterface $typed_config_manager) {
     if (!is_string($definition['id']) || !strlen($definition['id'])) {
       throw new \InvalidArgumentException(sprintf('The plugin type definition ID must be a non-empty string, but %s was given.', gettype($definition['id'])));
     }
@@ -121,6 +136,16 @@ class PluginType implements PluginTypeInterface {
       }
       $this->pluginDefinitionDecoratorClass = $definition['plugin_definition_decorator_class'];
     }
+    if (isset($definition['plugin_configuration_schema_id'])) {
+      if (!is_string($definition['plugin_configuration_schema_id'])) {
+        throw new \InvalidArgumentException(sprintf('The plugin type definition "plugin_configuration_schema_id" item must be a string, but %s was given.', gettype($definition['field_type'])));
+      }
+      $this->configurationSchemaId = $definition['plugin_configuration_schema_id'];
+    }
+    $plugin_configuration_schema_id = $this->getPluginConfigurationSchemaId('*');
+    if (!$typed_config_manager->hasConfigSchema($plugin_configuration_schema_id)) {
+      throw new \InvalidArgumentException(sprintf('The plugin type definition "plugin_configuration_schema_id" item references the configuration schema "%s" ("%s"), which does not exist.', $plugin_configuration_schema_id, $this->configurationSchemaId));
+    }
     $operations_provider_class = array_key_exists('operations_provider_class', $definition) ? $definition['operations_provider_class'] : DefaultPluginTypeOperationsProvider::class;
     $this->operationsProvider = $class_resolver->getInstanceFromDefinition($operations_provider_class);
     $this->pluginManager = $plugin_manager;
@@ -131,7 +156,7 @@ class PluginType implements PluginTypeInterface {
    * {@inheritdoc}
    */
   public static function createFromDefinition(ContainerInterface $container, array $definition) {
-    return new static($definition, $container->get('string_translation'), $container->get('class_resolver'), $container->get($definition['plugin_manager_service_id']));
+    return new static($definition, $container->get('string_translation'), $container->get('class_resolver'), $container->get($definition['plugin_manager_service_id']), $container->get('config.typed'));
   }
 
   /**
@@ -199,4 +224,11 @@ class PluginType implements PluginTypeInterface {
     return $this->fieldType;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginConfigurationSchemaId($plugin_id) {
+    return str_replace(['[plugin_type_id]', '[plugin_id]'], [$this->id, $plugin_id], $this->configurationSchemaId);
+  }
+
 }
diff --git a/tests/src/Unit/Controller/ListPluginTypesTest.php b/tests/src/Unit/Controller/ListPluginTypesTest.php
index 0db8d7c..084cc91 100644
--- a/tests/src/Unit/Controller/ListPluginTypesTest.php
+++ b/tests/src/Unit/Controller/ListPluginTypesTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\plugin\Unit\Controller;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\plugin\Controller\ListPluginTypes;
@@ -99,6 +100,11 @@ class ListPluginTypesTest extends UnitTestCase {
 
     $plugin_manager = $this->getMock(PluginManagerInterface::class);
 
+    $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class);
+    $typed_config_manager->expects($this->atLeastOnce())
+      ->method('hasConfigSchema')
+      ->willReturn(TRUE);
+
     $plugin_type_id_a = $this->randomMachineName();
     $plugin_type_label_a = $this->randomMachineName();
     $plugin_type_description_a = $this->randomMachineName();
@@ -108,7 +114,7 @@ class ListPluginTypesTest extends UnitTestCase {
       'description' => $plugin_type_description_a,
       'provider' => $this->randomMachineName(),
     ];
-    $plugin_type_a = new PluginType($plugin_type_definition_a, $this->stringTranslation, $class_resolver, $plugin_manager);
+    $plugin_type_a = new PluginType($plugin_type_definition_a, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager);
     $plugin_type_id_b = $this->randomMachineName();
     $plugin_type_label_b = $this->randomMachineName();
     $plugin_type_description_b = '';
@@ -118,7 +124,7 @@ class ListPluginTypesTest extends UnitTestCase {
       'description' => $plugin_type_description_b,
       'provider' => $this->randomMachineName(),
     ];
-    $plugin_type_b = new PluginType($plugin_type_definition_b, $this->stringTranslation, $class_resolver, $plugin_manager);
+    $plugin_type_b = new PluginType($plugin_type_definition_b, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager);
 
     $plugin_types = [
       $plugin_type_id_a => $plugin_type_a,
diff --git a/tests/src/Unit/Controller/ListPluginsTest.php b/tests/src/Unit/Controller/ListPluginsTest.php
index c264075..365ea91 100644
--- a/tests/src/Unit/Controller/ListPluginsTest.php
+++ b/tests/src/Unit/Controller/ListPluginsTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\plugin\Unit\Controller;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\plugin\Controller\ListPlugins;
@@ -101,6 +102,11 @@ class ListPluginsTest extends UnitTestCase {
 
     $plugin_manager = $this->getMock(PluginManagerInterface::class);
 
+    $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class);
+    $typed_config_manager->expects($this->atLeastOnce())
+      ->method('hasConfigSchema')
+      ->willReturn(TRUE);
+
     $plugin_type_id = $this->randomMachineName();
     $plugin_type_label = $this->randomMachineName();
 
@@ -109,7 +115,7 @@ class ListPluginsTest extends UnitTestCase {
       'label' => $plugin_type_label,
       'provider' => $this->randomMachineName(),
     ];
-    $plugin_type = new PluginType($plugin_type_definition, $this->stringTranslation, $class_resolver, $plugin_manager);
+    $plugin_type = new PluginType($plugin_type_definition, $this->stringTranslation, $class_resolver, $plugin_manager, $typed_config_manager);
 
     $this->pluginTypeManager->expects($this->atLeastOnce())
       ->method('getPluginType')
diff --git a/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php b/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php
index 3154a0c..9753bc2 100644
--- a/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php
+++ b/tests/src/Unit/Plugin/Field/FieldType/PluginCollectionItemDeriverTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\plugin\Unit\Plugin\Field\FieldType;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\plugin\Plugin\Field\FieldType\PluginCollectionItemDeriver;
 use Drupal\plugin\PluginType\PluginType;
@@ -71,6 +72,11 @@ class PluginCollectionItemDeriverTest extends UnitTestCase {
 
     $plugin_manager = $this->getMock(PluginManagerInterface::class);
 
+    $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class);
+    $typed_config_manager->expects($this->atLeastOnce())
+      ->method('hasConfigSchema')
+      ->willReturn(TRUE);
+
     $provider = $this->randomMachineName();
 
     $plugin_type_id_a = $this->randomMachineName();
@@ -82,7 +88,7 @@ class PluginCollectionItemDeriverTest extends UnitTestCase {
       'description' => $plugin_type_description_a,
       'provider' => $this->randomMachineName(),
     ];
-    $plugin_type_a = new PluginType($plugin_type_definition_a, $string_translation, $class_resolver, $plugin_manager);
+    $plugin_type_a = new PluginType($plugin_type_definition_a, $string_translation, $class_resolver, $plugin_manager, $typed_config_manager);
     $plugin_type_id_b = $this->randomMachineName();
     $plugin_type_label_b = $this->randomMachineName();
     $plugin_type_description_b = '';
@@ -92,7 +98,7 @@ class PluginCollectionItemDeriverTest extends UnitTestCase {
       'description' => $plugin_type_description_b,
       'provider' => $this->randomMachineName(),
     ];
-    $plugin_type_b = new PluginType($plugin_type_definition_b, $string_translation, $class_resolver, $plugin_manager);
+    $plugin_type_b = new PluginType($plugin_type_definition_b, $string_translation, $class_resolver, $plugin_manager, $typed_config_manager);
 
     $plugin_types = [$plugin_type_a, $plugin_type_b];
 
diff --git a/tests/src/Unit/PluginType/PluginTypeTest.php b/tests/src/Unit/PluginType/PluginTypeTest.php
index 1e729ef..2fe775a 100644
--- a/tests/src/Unit/PluginType/PluginTypeTest.php
+++ b/tests/src/Unit/PluginType/PluginTypeTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\plugin\Unit\PluginType;
 
 use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator;
 use Drupal\plugin\PluginDefinition\PluginDefinitionDecoratorInterface;
@@ -63,11 +64,17 @@ class PluginTypeTest extends UnitTestCase {
 
     $class_resolver = $this->getMock(ClassResolverInterface::class);
 
+    $typed_config_manager = $this->getMock(TypedConfigManagerInterface::class);
+    $typed_config_manager->expects($this->atLeastOnce())
+      ->method('hasConfigSchema')
+      ->willReturn(TRUE);
+
     $this->pluginManager = $this->getMock(PluginManagerInterface::class);
 
     $this->container = $this->getMock(ContainerInterface::class);
     $map = [
       ['class_resolver', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $class_resolver],
+      ['config.typed', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $typed_config_manager],
       ['string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->getStringTranslationStub()],
       [$this->pluginTypeDefinition['plugin_manager_service_id'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->pluginManager],
     ];
@@ -129,6 +136,27 @@ class PluginTypeTest extends UnitTestCase {
   }
 
   /**
+   * @covers ::getPluginConfigurationSchemaId
+   */
+  public function testGetPluginConfigurationSchemaIdWithDefaultId() {
+    $plugin_id = 'FooBarQux';
+    $expected_schema_id = sprintf('plugin.plugin_configuration.%s.%s', $this->pluginTypeDefinition['id'], $plugin_id);
+    $this->assertSame($expected_schema_id, $this->sut->getPluginConfigurationSchemaId($plugin_id));
+  }
+
+  /**
+   * @covers ::getPluginConfigurationSchemaId
+   */
+  public function testGetPluginConfigurationSchemaIdWithDefinedId() {
+    $plugin_id = 'FooBarQux';
+    $schema_id = 'foo_bar.qux.[plugin_id]';
+    $this->pluginTypeDefinition['plugin_configuration_schema_id'] = $schema_id;
+    $this->sut = PluginType::createFromDefinition($this->container, $this->pluginTypeDefinition);
+    $expected_schema_id = 'foo_bar.qux.' . $plugin_id;
+    $this->assertSame($expected_schema_id, $this->sut->getPluginConfigurationSchemaId($plugin_id));
+  }
+
+  /**
    * @covers ::ensureTypedPluginDefinition
    * @covers ::createFromDefinition
    * @covers ::__construct
