diff --git a/core/modules/field/src/BaseFieldStorageConfigWrapper.php b/core/modules/field/src/BaseFieldStorageConfigWrapper.php
new file mode 100644
index 0000000000..05bcf6df68
--- /dev/null
+++ b/core/modules/field/src/BaseFieldStorageConfigWrapper.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Drupal\field;
+
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Entity\FieldStorageConfig;
+
+/**
+ * Wrapper to allow base fields to be handled as fields provided by the Field
+ * module when dealing with code special casing them.
+ *
+ * @internal
+ *   This should be removed as soon all core APIs uniformly handle entity fields
+ *   regardless of their underlying implementations.
+ */
+class BaseFieldStorageConfigWrapper extends FieldStorageConfig {
+
+  /**
+   * An associative array of field bundle IDs keyed by entity type ID and field
+   * name.
+   *
+   * @var string[][][]
+   */
+  protected static $field_bundle_ids;
+
+  /**
+   * Constructs a wrapper from a base field definition.
+   *
+   * @param \Drupal\Core\Field\BaseFieldDefinition $base_field_definition
+   *   A base field definition.
+   *
+   * @return static
+   *   A wrapper instance.
+   */
+  public static function createFromBaseFieldDefinition(BaseFieldDefinition $base_field_definition) {
+    $values = $base_field_definition->toArray();
+    $values['type'] = $base_field_definition->getType();
+    $values['module'] = static::lookupTypeProvider($values['type']);
+    return new static($values);
+  }
+
+  /**
+   * Returns the field type provider.
+   *
+   * @param string $type
+   *   The field type.
+   *
+   * @return string
+   *   The field type provider.
+   */
+  protected static function lookupTypeProvider($type) {
+    /** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
+    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
+    try {
+      $field_type = $field_type_manager->getDefinition($type);
+      return $field_type['provider'];
+    }
+    catch (PluginNotFoundException $e) {
+      return NULL;
+    }
+  }
+
+  /**
+   * Returns field bundles for the specified entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return string[][]
+   *   An associative array of bundle IDs keyed by field name.
+   */
+  public static function getFieldBundles($entity_type_id) {
+    $field_bundles = &static::$field_bundle_ids[$entity_type_id];
+    if (!isset($field_bundles)) {
+      $field_bundles = [];
+      /** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info */
+      $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type_id);
+      /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
+      $entity_field_manager = \Drupal::service('entity_field.manager');
+      foreach ($bundle_info as $bundle_id => $info) {
+        /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
+        foreach ($entity_field_manager->getFieldDefinitions($entity_type_id, $bundle_id) as $field_name => $field_definition) {
+          // Filter out Field module's field definitions.
+          if (!($field_definition instanceof FieldConfigInterface) && $field_definition instanceof FieldStorageDefinitionInterface && !$field_definition->isBaseField()) {
+            $field_bundles[$field_name][] = $bundle_id;
+          }
+        }
+      }
+    }
+    return $field_bundles;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundles() {
+    $field_bundles = static::getFieldBundles($this->entity_type);
+    return isset($field_bundles[$this->field_name]) ? $field_bundles[$this->field_name] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSave(EntityStorageInterface $storage) {
+    throw new \BadMethodCallException('BaseFieldStorageConfigWrapper instances cannot be persisted.');
+  }
+
+}
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index 079ee3bb3d..4fdb28e4e1 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -9,9 +9,12 @@
 use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
 use Drupal\Core\Entity\Sql\TableMappingInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\field\BaseFieldStorageConfigWrapper;
+use Drupal\field\Entity\FieldStorageConfigWrapper;
 use Symfony\Component\DependencyInjection\Container;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -321,6 +324,19 @@ public function getViewsData() {
       $table_data['table']['entity type'] = $entity_type_id;
     });
 
+    // Add any bundle fields defined in code.
+    // @todo Here we are assuming that hard-coded bundle field definitions
+    //   extend BaseFieldDefinition, which is a common case. Revisit this logic
+    //   in https://www.drupal.org/project/drupal/issues/2930736.
+    $storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityType->id());
+    foreach (BaseFieldStorageConfigWrapper::getFieldBundles($this->entityType->id()) as $field_name => $bundles) {
+      $storage_definition = isset($storage_definitions[$field_name]) ? $storage_definitions[$field_name] : NULL;
+      if (!$storage_definition->isBaseField() && $storage_definition instanceof BaseFieldDefinition) {
+        $field_storage = BaseFieldStorageConfigWrapper::createFromBaseFieldDefinition($storage_definition);
+        views_collect_field_view_data($field_storage, $data);
+      }
+    }
+
     return $data;
   }
 
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index 633ce9b9da..15acd107a8 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -171,31 +171,44 @@ function views_views_data() {
     }
   }
 
-  // Field modules can implement hook_field_views_data() to override the default
-  // behavior for adding fields.
-  $module_handler = \Drupal::moduleHandler();
-
   $entity_manager = \Drupal::entityManager();
   if ($entity_manager->hasDefinition('field_storage_config')) {
     /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
     foreach ($entity_manager->getStorage('field_storage_config')->loadMultiple() as $field_storage) {
-      if (_views_field_get_entity_type_storage($field_storage)) {
-        $result = (array) $module_handler->invoke($field_storage->getTypeProvider(), 'field_views_data', [$field_storage]);
-        if (empty($result)) {
-          $result = views_field_default_views_data($field_storage);
-        }
-        $module_handler->alter('field_views_data', $result, $field_storage);
-
-        if (is_array($result)) {
-          $data = NestedArray::mergeDeep($result, $data);
-        }
-      }
+      views_collect_field_view_data($field_storage, $data);
     }
   }
 
   return $data;
 }
 
+/**
+ * Collects view data for the specified Field module field.
+ *
+ * @param \Drupal\field\FieldStorageConfigInterface $field_storage
+ *   The field storage definition.
+ * @param array $data
+ *   A reference to a view data array.
+ *
+ * @internal
+ */
+function views_collect_field_view_data(FieldStorageConfigInterface $field_storage, array &$data) {
+  // Field modules can implement hook_field_views_data() to override the default
+  // behavior for adding fields.
+  $module_handler = \Drupal::moduleHandler();
+  if (_views_field_get_entity_type_storage($field_storage)) {
+    $result = (array) $module_handler->invoke($field_storage->getTypeProvider(), 'field_views_data', [$field_storage]);
+    if (empty($result)) {
+      $result = views_field_default_views_data($field_storage);
+    }
+    $module_handler->alter('field_views_data', $result, $field_storage);
+
+    if (is_array($result)) {
+      $data = NestedArray::mergeDeep($result, $data);
+    }
+  }
+}
+
 /**
  * Implements hook_views_data_alter().
  *
@@ -361,7 +374,7 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora
   $untranslatable_config_bundles = [];
 
   foreach ($bundles_names as $bundle) {
-    $fields[$bundle] = FieldConfig::loadByName($entity_type->id(), $bundle, $field_name);
+    $fields[$bundle] = $entity_manager->getFieldDefinitions($entity_type->id(), $bundle)[$field_name];
   }
   foreach ($fields as $bundle => $config_entity) {
     if (!empty($config_entity)) {
