diff --git a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts
index 45526ace9c7..07337492994 100644
--- a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts
+++ b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts
@@ -13,7 +13,7 @@ import Adapter from '@ember-data/adapter';
 import Serializer from '@ember-data/serializer';
 import { resolve, all } from 'rsvp';
 import { ExistingResourceObject } from '@ember-data/store/-private/ts-interfaces/ember-data-json-api';
-import { Dict } from '@ember-data/store/-private/ts-interfaces/utils';
+import { ConfidentDict } from '@ember-data/store/-private/ts-interfaces/utils';
 import { StableRecordIdentifier } from '@ember-data/store/-private/ts-interfaces/identifier';
 import { identifierCacheFor } from '@ember-data/store/-private';
 import { set } from '@ember/object';
@@ -30,8 +30,8 @@ if (IDENTIFIERS) {
       let store;
       let calls;
       let secondaryCache: {
-        id: Dict<string, string>;
-        username: Dict<string, string>;
+        id: ConfidentDict<string>;
+        username: ConfidentDict<string>;
       };
       class TestSerializer extends Serializer {
         normalizeResponse(_, __, payload) {
@@ -232,7 +232,7 @@ if (IDENTIFIERS) {
     module('Secondary Cache using an attribute as an alternate id', function(hooks) {
       let store;
       let calls;
-      let secondaryCache: Dict<string, string>;
+      let secondaryCache: ConfidentDict<string>;
       class TestSerializer extends Serializer {
         normalizeResponse(_, __, payload) {
           return payload;
diff --git a/packages/adapter/addon/-private/index.js b/packages/adapter/addon/-private/index.js
index e244a107c58..4d1d18fb8df 100644
--- a/packages/adapter/addon/-private/index.js
+++ b/packages/adapter/addon/-private/index.js
@@ -7,3 +7,4 @@ export { determineBodyPromise } from './utils/determine-body-promise';
 export { serializeQueryParams } from './utils/serialize-query-params';
 export { default as fetch } from './utils/fetch';
 export { default as BuildURLMixin } from './build-url-mixin';
+export { default as serializeIntoHash } from './utils/serialize-into-hash';
diff --git a/packages/adapter/addon/-private/utils/serialize-into-hash.js b/packages/adapter/addon/-private/utils/serialize-into-hash.js
new file mode 100644
index 00000000000..daa20881f70
--- /dev/null
+++ b/packages/adapter/addon/-private/utils/serialize-into-hash.js
@@ -0,0 +1,11 @@
+export default function serializeIntoHash(store, modelClass, snapshot, options = { includeId: true }) {
+  const serializer = store.serializerFor(modelClass.modelName);
+
+  if (typeof serializer.serializeIntoHash === 'function') {
+    const data = {};
+    serializer.serializeIntoHash(data, modelClass, snapshot, options);
+    return data;
+  }
+
+  return serializer.serialize(snapshot, options);
+}
diff --git a/packages/adapter/addon/json-api.js b/packages/adapter/addon/json-api.js
index e473453a413..dc3b2bfa820 100644
--- a/packages/adapter/addon/json-api.js
+++ b/packages/adapter/addon/json-api.js
@@ -4,6 +4,7 @@
 import { dasherize } from '@ember/string';
 import RESTAdapter from './rest';
 import { pluralize } from 'ember-inflector';
+import { serializeIntoHash } from './-private';
 
 /**
   The `JSONAPIAdapter` is the default adapter used by Ember Data. It
@@ -227,12 +228,8 @@ const JSONAPIAdapter = RESTAdapter.extend({
     return pluralize(dasherized);
   },
 
-  // TODO: Remove this once we have a better way to override HTTP verbs.
   updateRecord(store, type, snapshot) {
-    let data = {};
-    let serializer = store.serializerFor(type.modelName);
-
-    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });
+    const data = serializeIntoHash(store, type, snapshot);
 
     let url = this.buildURL(type.modelName, snapshot.id, snapshot, 'updateRecord');
 
diff --git a/packages/adapter/addon/rest.js b/packages/adapter/addon/rest.js
index 763f56a8234..f0f9c86f671 100644
--- a/packages/adapter/addon/rest.js
+++ b/packages/adapter/addon/rest.js
@@ -23,6 +23,7 @@ import AdapterError, {
 } from '@ember-data/adapter/error';
 import { warn } from '@ember/debug';
 import { DEBUG } from '@glimmer/env';
+import { serializeIntoHash } from './-private';
 
 const Promise = EmberPromise;
 const hasJQuery = typeof jQuery !== 'undefined';
@@ -723,13 +724,11 @@ const RESTAdapter = Adapter.extend(BuildURLMixin, {
     @return {Promise} promise
   */
   createRecord(store, type, snapshot) {
-    let data = {};
-    let serializer = store.serializerFor(type.modelName);
     let url = this.buildURL(type.modelName, null, snapshot, 'createRecord');
 
-    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });
+    const data = serializeIntoHash(store, type, snapshot);
 
-    return this.ajax(url, 'POST', { data: data });
+    return this.ajax(url, 'POST', { data });
   },
 
   /**
@@ -749,15 +748,12 @@ const RESTAdapter = Adapter.extend(BuildURLMixin, {
     @return {Promise} promise
   */
   updateRecord(store, type, snapshot) {
-    let data = {};
-    let serializer = store.serializerFor(type.modelName);
-
-    serializer.serializeIntoHash(data, type, snapshot);
+    const data = serializeIntoHash(store, type, snapshot, {});
 
     let id = snapshot.id;
     let url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');
 
-    return this.ajax(url, 'PUT', { data: data });
+    return this.ajax(url, 'PUT', { data });
   },
 
   /**
diff --git a/packages/store/addon/-private/identifiers/cache.ts b/packages/store/addon/-private/identifiers/cache.ts
index 3764a3e876f..d25ef6ea242 100644
--- a/packages/store/addon/-private/identifiers/cache.ts
+++ b/packages/store/addon/-private/identifiers/cache.ts
@@ -1,6 +1,6 @@
 import { DEBUG } from '@glimmer/env';
 import { warn } from '@ember/debug';
-import { Dict } from '../ts-interfaces/utils';
+import { ConfidentDict } from '../ts-interfaces/utils';
 import { ResourceIdentifierObject, ExistingResourceObject } from '../ts-interfaces/ember-data-json-api';
 import {
   StableRecordIdentifier,
@@ -30,8 +30,8 @@ interface KeyOptions {
   _allIdentifiers: StableRecordIdentifier[];
 }
 
-type IdentifierMap = Dict<string, StableRecordIdentifier>;
-type TypeMap = Dict<string, KeyOptions>;
+type IdentifierMap = ConfidentDict<StableRecordIdentifier>;
+type TypeMap = ConfidentDict<KeyOptions>;
 export type MergeMethod = (
   targetIdentifier: StableRecordIdentifier,
   matchedIdentifier: StableRecordIdentifier,
diff --git a/packages/store/addon/-private/system/fetch-manager.ts b/packages/store/addon/-private/system/fetch-manager.ts
index 7551acc481a..d13f730eb92 100644
--- a/packages/store/addon/-private/system/fetch-manager.ts
+++ b/packages/store/addon/-private/system/fetch-manager.ts
@@ -9,7 +9,6 @@ import { serializerForAdapter } from './store/serializers';
 import { InvalidError } from '@ember-data/adapter/error';
 import coerceId from './coerce-id';
 import { A } from '@ember/array';
-
 import { _findHasMany, _findBelongsTo, _findAll, _query, _queryRecord } from './store/finders';
 import RequestCache from './request-cache';
 import { CollectionResourceDocument, SingleResourceDocument } from '../ts-interfaces/ember-data-json-api';
@@ -135,7 +134,14 @@ export default class FetchManager {
       },
       function(error) {
         if (error instanceof InvalidError) {
-          let parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
+          let parsedErrors = error.errors;
+
+          if (typeof serializer.extractErrors === 'function') {
+            parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
+          } else {
+            parsedErrors = errorsArrayToHash(error.errors);
+          }
+
           throw { error, parsedErrors };
         } else {
           throw { error };
diff --git a/packages/store/addon/-private/system/identity-map.ts b/packages/store/addon/-private/system/identity-map.ts
index f689f7e5847..01063192552 100644
--- a/packages/store/addon/-private/system/identity-map.ts
+++ b/packages/store/addon/-private/system/identity-map.ts
@@ -1,5 +1,5 @@
 import InternalModelMap from './internal-model-map';
-import { Dict } from '../ts-interfaces/utils';
+import { ConfidentDict } from '../ts-interfaces/utils';
 
 /**
   @module @ember-data/store
@@ -13,7 +13,7 @@ import { Dict } from '../ts-interfaces/utils';
  @private
  */
 export default class IdentityMap {
-  private _map: Dict<string, InternalModelMap> = Object.create(null);
+  private _map: ConfidentDict<InternalModelMap> = Object.create(null);
 
   /**
    Retrieves the `InternalModelMap` for a given modelName,
diff --git a/packages/store/addon/-private/system/internal-model-map.ts b/packages/store/addon/-private/system/internal-model-map.ts
index 85c2a4e0006..3d0c48dfcf6 100644
--- a/packages/store/addon/-private/system/internal-model-map.ts
+++ b/packages/store/addon/-private/system/internal-model-map.ts
@@ -1,6 +1,6 @@
 import { assert } from '@ember/debug';
 import InternalModel from './model/internal-model';
-import { Dict } from '../ts-interfaces/utils';
+import { ConfidentDict } from '../ts-interfaces/utils';
 
 /**
   @module @ember-data/store
@@ -17,9 +17,9 @@ import { Dict } from '../ts-interfaces/utils';
  @private
  */
 export default class InternalModelMap {
-  private _idToModel: Dict<string, InternalModel> = Object.create(null);
+  private _idToModel: ConfidentDict<InternalModel> = Object.create(null);
   private _models: InternalModel[] = [];
-  private _metadata: Dict<string, any> | null = null;
+  private _metadata: ConfidentDict<any> | null = null;
 
   constructor(public modelName: string) {}
 
@@ -104,7 +104,7 @@ export default class InternalModelMap {
    * @property metadata
    * @type Object
    */
-  get metadata(): Dict<string, any> {
+  get metadata(): ConfidentDict<any> {
     return this._metadata || (this._metadata = Object.create(null));
   }
 
diff --git a/packages/store/addon/-private/system/model/internal-model.ts b/packages/store/addon/-private/system/model/internal-model.ts
index b6414a9ecef..2f305bd8d31 100644
--- a/packages/store/addon/-private/system/model/internal-model.ts
+++ b/packages/store/addon/-private/system/model/internal-model.ts
@@ -23,7 +23,7 @@ import { default as recordDataFor, relationshipStateFor } from '../record-data-f
 import RecordData from '../../ts-interfaces/record-data';
 import { JsonApiResource, JsonApiValidationError } from '../../ts-interfaces/record-data-json-api';
 import { Record } from '../../ts-interfaces/record';
-import { Dict } from '../../ts-interfaces/utils';
+import { ConfidentDict } from '../../ts-interfaces/utils';
 import { IDENTIFIERS, RECORD_DATA_ERRORS, RECORD_DATA_STATE, REQUEST_SERVICE } from '@ember-data/canary-features';
 import { identifierCacheFor } from '../../identifiers/cache';
 import { StableRecordIdentifier } from '../../ts-interfaces/identifier';
@@ -107,14 +107,14 @@ export default class InternalModel {
   __recordArrays: any;
   _references: any;
   _recordReference: any;
-  _manyArrayCache: Dict<string, ManyArray> = Object.create(null);
+  _manyArrayCache: ConfidentDict<ManyArray> = Object.create(null);
 
   // The previous ManyArrays for this relationship which will be destroyed when
   // we create a new ManyArray, but in the interim the retained version will be
   // updated if inverse internal models are unloaded.
-  _retainedManyArrayCache: Dict<string, ManyArray> = Object.create(null);
-  _relationshipPromisesCache: Dict<string, RSVP.Promise<any>> = Object.create(null);
-  _relationshipProxyCache: Dict<string, PromiseManyArray | PromiseBelongsTo> = Object.create(null);
+  _retainedManyArrayCache: ConfidentDict<ManyArray> = Object.create(null);
+  _relationshipPromisesCache: ConfidentDict<RSVP.Promise<any>> = Object.create(null);
+  _relationshipProxyCache: ConfidentDict<PromiseManyArray | PromiseBelongsTo> = Object.create(null);
   currentState: any;
   error: any;
 
diff --git a/packages/store/addon/-private/system/store.ts b/packages/store/addon/-private/system/store.ts
index 3c925be573b..faf07187d8e 100644
--- a/packages/store/addon/-private/system/store.ts
+++ b/packages/store/addon/-private/system/store.ts
@@ -61,7 +61,6 @@ import hasValidId from '../utils/has-valid-id';
 import { RequestPromise } from './request-cache';
 import { PromiseProxy } from '../ts-interfaces/promise-proxies';
 import { DSModel } from '../ts-interfaces/ds-model';
-
 const emberRun = emberRunLoop.backburner;
 
 const { ENV } = Ember;
@@ -3298,7 +3297,14 @@ function _commit(adapter, store, operation, snapshot) {
     },
     function(error) {
       if (error instanceof InvalidError) {
-        let parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
+        let parsedErrors;
+
+        if (typeof serializer.extractErrors === 'function') {
+          parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
+        } else {
+          parsedErrors = errorsArrayToHash(error.errors);
+        }
+
         store.recordWasInvalid(internalModel, parsedErrors, error);
       } else {
         store.recordWasError(internalModel, error);
diff --git a/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts b/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts
index d5c39f50e9e..7fadb3b0937 100644
--- a/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts
+++ b/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts
@@ -5,7 +5,7 @@ import { Dict } from './utils';
   @module @ember-data/store
 */
 
-export type Meta = Dict<string, JSONValue>;
+export type Meta = Dict<JSONValue>;
 
 /**
  * Serves as a reference to a `Resource` but does not contain
@@ -74,15 +74,15 @@ export type ResourceIdentifierObject = ExistingResourceIdentifierObject | NewRes
  * Contains the data for an existing resource in JSON:API format
  */
 export interface ExistingResourceObject extends ExistingResourceIdentifierObject {
-  meta?: Dict<string, JSONValue>;
-  attributes?: Dict<string, JSONValue>;
+  meta?: Dict<JSONValue>;
+  attributes?: Dict<JSONValue>;
   // these are lossy, need improved typing
-  relationships?: Dict<string, JSONValue>;
-  links?: Dict<string, JSONValue>;
+  relationships?: Dict<JSONValue>;
+  links?: Dict<JSONValue>;
 }
 
 interface Document {
-  meta?: Dict<string, JSONValue>;
+  meta?: Dict<JSONValue>;
   included?: ExistingResourceObject[];
 }
 
diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts
new file mode 100644
index 00000000000..f6819d9c2d7
--- /dev/null
+++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts
@@ -0,0 +1,319 @@
+/**
+  ## Overview
+
+  In order to properly manage and present your data, `EmberData`
+  needs to understand the structure of data it receives.
+
+  `Serializers` convert data between the server's API format and
+  the format `EmberData` understands.
+
+  Data received from an API response is `"normalized"` into 
+  [JSON:API](https://jsonapi.org/) (the format used internally
+  by `EmberData`), while data sent to an API is `"serialized"`
+  into the format the API expects.
+
+  ### Implementing a Serializer
+
+  There are only two required serializer methods, one for
+  normalizing data from the server API format into `JSON:API`, and
+  another for serializing records via `Snapshot`s into the expected
+  server API format.
+
+  To implement a serializer, export a class that conforms to the structure
+  described by the [MinimumSerializerInterface](MinimumSerializerInterface)
+  from the `app/serializers/` directory. An example is below.
+
+  ```ts
+  import EmberObject from '@ember/object';
+
+  export default class ApplicationSerializer extends EmberObject {
+    normalizeResponse(store, schema, rawPayload) {
+      return rawPayload;
+    }
+
+    serialize(snapshot, options) {
+      const serializedResource = {
+        id: snapshot.id(),
+        type: snapshot.modelName,
+        attributes: snapshot.attributes()
+      };
+
+      return serializedResource;
+    }
+  }
+ ```
+
+
+  #### Serializer Resolution
+
+  `store.serializerFor(name)` will lookup serializers defined in
+  `app/serializers` and return an instance. If no serializer is found, an
+  error will be thrown.
+
+  `serializerFor` first attempts to find a serializer with an exact match on `name`,
+  then falls back to checking for the presence of a serializer named `application`.
+
+  ```ts
+  store.serializerFor('author');
+
+  // lookup paths (in order) =>
+  //   app/serializers/author.js
+  //   app/serializers/application.js
+  ```
+
+  Most requests in `ember-data` are made with respect to a particular `type` (or `modelName`)
+  (e.g., "get me the full collection of **books**" or "get me the **employee** whose id is 37"). We
+  refer to this as the *"primary"* resource `type`.
+  
+  Typically `serializerFor` will be used to find a serializer with a name matching that of the primary
+  resource `type` for the request, falling back to the `application` serializer for those types that
+  do not have a defined serializer. This is often described as a `per-model` or `per-type` strategy
+  for defining serializers. However, because APIs rarely format payloads per-type but rather
+  per-API-version, this may not be a desired strategy.
+
+  It is recommended that applications define only a single `application` adapter and serializer
+  where possible.
+
+  If you have multiple API formats and the per-type strategy is not viable, one strategy is to
+  write an `application` adapter and serializer that make use of `options` to specify the desired
+  format when making a request.
+
+  ### Using a Serializer
+
+  Any serializer in `app/serializers` can be looked up by `name` using `store.serializerFor(name)`.
+
+  ### Default Serializers
+
+  For applications whose APIs are *very close to* or *exactly* the `REST` or `JSON:API`
+  format the `@ember-data/serializer` package contains implementations these applications can
+  extend. It also contains a simple `JSONSerializer` for serializing to/from very basic JSON objects.
+
+  Many applications will find writing their own serializer to be more performant and less
+  complex than extending these classes even when their API format is very close to that expected
+  by these serializers.
+
+  It is recommended that apps write their own serializer to best suit the needs of their API and
+  application.
+
+  @module @ember-data/serializer
+  @main @ember-data/serializer
+  @class MinimumSerializerInterface
+  @public
+*/
+
+import { Object as JSONObject } from 'json-typescript';
+import Store from '../system/core-store';
+import { JsonApiDocument, SingleResourceDocument } from './ember-data-json-api';
+import Snapshot from '../system/snapshot';
+import ShimModelClass from '../system/model/shim-model-class';
+import { Dict } from './utils';
+
+type OptionsHash = Dict<any>;
+
+interface Serializer {
+  /**
+   * This method is responsible for normalizing the value resolved from the promise returned
+   * by an Adapter request into the format expected by the `Store`.
+   *
+   * The output should be a [JSON:API Document](https://jsonapi.org/format/#document-structure)
+   * with the following additional restrictions:
+   *
+   * - `type` should be formatted in the `singular` `dasherized` `lowercase` form
+   * - `members` (the property names of attributes and relationships) should be formatted
+   *    to match their definition in the corresponding `Model` definition. Typically this
+   *    will be `camelCase`.
+   * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is
+   *    a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects)
+   *    and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects)
+   *
+   * @method normalizeResponse
+   * @public
+   * @param {Store} store - the store service that initiated the request being normalized
+   * @param {ShimModelClass} schema - An object with methods for accessing information about
+   *  the type, attributes and relationships of the primary type associated with the request.
+   * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request.
+   *  This correlates to the value the promise returned by the adapter method that performed
+   *  the request resolved to.
+   * @param {string|null} id - For a `findRecord` request, this is the `id` initially provided
+   *  in the call to `store.findRecord`. Else this value is `null`.
+   * @param {'findRecord' | 'queryRecord' | 'findAll' | 'findBelongsTo' | 'findHasMany' | 'findMany' | 'query' | 'createRecord' | 'deleteRecord' | 'updateRecord'} requestType - The
+   *  type of request the Adapter had been asked to perform.
+   *
+   * @returns {JsonApiDocument} - a document following the structure of a [JSON:API Document](https://jsonapi.org/format/#document-structure).
+   */
+  normalizeResponse(
+    store: Store,
+    schema: ShimModelClass,
+    rawPayload: JSONObject,
+    id: string | null,
+    requestType:
+      | 'findRecord'
+      | 'queryRecord'
+      | 'findAll'
+      | 'findBelongsTo'
+      | 'findHasMany'
+      | 'findMany'
+      | 'query'
+      | 'createRecord'
+      | 'deleteRecord'
+      | 'updateRecord'
+  ): JsonApiDocument;
+
+  /**
+   * This method is responsible for serializing an individual record
+   * via a [Snapshot](Snapshot) into the format expected by the API.
+   *
+   * This method is called by `snapshot.serialize()`.
+   *
+   * When using `Model`, this method is called by `record.serialize()`.
+   *
+   * When using `JSONAPIAdapter` or `RESTAdapter` this method is called
+   * by `updateRecord` and `createRecord` if `Serializer.serializeIntoHash`
+   * is not implemented.
+   *
+   * @method serialize
+   * @public
+   * @param {Snapshot} snapshot - A Snapshot for the record to serialize
+   * @param {object} [options]
+   */
+  serialize(snapshot: Snapshot, options?: OptionsHash): JSONObject;
+
+  /**
+   * This method is intended to normalize data into a [JSON:API Document](https://jsonapi.org/format/#document-structure)
+   * with a data member containing a single [Resource](https://jsonapi.org/format/#document-resource-objects).
+   *
+   * - `type` should be formatted in the `singular` `dasherized` `lowercase` form
+   * - `members` (the property names of attributes and relationships) should be formatted
+   *    to match their definition in the corresponding `Model` definition. Typically this
+   *    will be `camelCase`.
+   * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is
+   *    a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects)
+   *    and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects)
+   *
+   * This method is called by the `Store` when `store.normalize(modelName, payload)` is
+   * called. It is recommended to use `store.serializerFor(modelName).normalizeResponse`
+   * over `store.normalize`.
+   *
+   * This method may be called when also using the `RESTSerializer`
+   * when `serializer.pushPayload` is called by `store.pushPayload`.
+   * It is recommended to use `store.push` over `store.pushPayload` after normalizing
+   * the payload directly.
+   *
+   * Example:
+   * ```js
+   * function pushPayload(store, modelName, rawPayload) {
+   *   const ModelClass = store.modelFor(modelName);
+   *   const serializer = store.serializerFor(modelName);
+   *   const jsonApiPayload = serializer.normalizeResponse(store, ModelClass, rawPayload, null, 'query');
+   *
+   *   return store.push(jsonApiPayload);
+   * }
+   * ```
+   *
+   * This method may be called when also using the `JSONAPISerializer`
+   * when normalizing included records. If mixing serializer usage in this way
+   * we recommend implementing this method, but caution that it may lead
+   * to unexpected mixing of formats.
+   *
+   * This method may also be called when normalizing embedded relationships when
+   * using the `EmbeddedRecordsMixin`. If using this mixin in a serializer in
+   * your application we recommend implementing this method, but caution that
+   * it may lead to unexpected mixing of formats.
+   *
+   * @method normalize [OPTIONAL]
+   * @public
+   * @optional
+   * @param {ShimModelClass} schema - An object with methods for accessing information about
+   *  the type, attributes and relationships of the primary type associated with the request.
+   * @param {JSONObject} rawPayload - Some raw JSON data to be normalized into a [JSON:API Resource](https://jsonapi.org/format/#document-resource-objects).
+   * @param {string} [prop] - When called by the `EmbeddedRecordsMixin` this param will be the
+   *  property at which the object provided as rawPayload was found.
+   * @returns {SingleResourceDocument} - A [JSON:API Document](https://jsonapi.org/format/#document-structure)
+   *  containing a single [JSON:API Resource](https://jsonapi.org/format/#document-resource-objects)
+   *  as its primary data.
+   */
+  normalize?(schema: ShimModelClass, rawPayload: JSONObject, prop?: string): SingleResourceDocument;
+
+  /**
+   * When using `JSONAPIAdapter` or `RESTAdapter` this method is called
+   * by `adapter.updateRecord` and `adapter.createRecord` if `Serializer.serializeIntoHash`
+   * is not implemented.
+   *
+   * You can use this method to customize the root keys serialized into the payload.
+   * The hash property should be modified by reference.
+   *
+   * For instance, your API may expect resources to be keyed by underscored type in the payload:
+   *
+   * ```js
+   * {
+   *   _user: {
+   *     type: 'user',
+   *     id: '1'
+   *   }
+   * }
+   * ```
+   *
+   * Which when using these adapters can be achieved by implementing this method similar
+   * to the following:
+   *
+   * ```js
+   * serializeIntoHash(hash, ModelClass, snapshot, options) {
+   *   hash[`_${snapshot.modelName}`] = this.serialize(snapshot, options).data;
+   * }
+   * ```
+   *
+   * @method serializeIntoHash [OPTIONAL]
+   * @public
+   * @optional
+   * @param hash - a top most object of the request payload onto
+   *  which to append the serialized record
+   * @param {ShimModelClass} schema - An object with methods for accessing information about
+   *  the type, attributes and relationships of the primary type associated with the request.
+   * @param {Snapshot} snapshot - A Snapshot for the record to serialize
+   * @param [options]
+   * @returns {void}
+   */
+  serializeIntoHash?(hash: object, schema: ShimModelClass, snapshot: Snapshot, options?: OptionsHash): void;
+
+  /**
+   * This method allows for normalization of data when `store.pushPayload` is called
+   * and should be implemented if you want to use that method.
+   *
+   * The output should be a [JSON:API Document](https://jsonapi.org/format/#document-structure)
+   * with the following additional restrictions:
+   *
+   * - `type` should be formatted in the `singular` `dasherized` `lowercase` form
+   * - `members` (the property names of attributes and relationships) should be formatted
+   *    to match their definition in the corresponding `Model` definition. Typically this
+   *    will be `camelCase`.
+   * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is
+   *    a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects)
+   *    and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects)
+   *
+   * If you need better control over normalization or want access to the records being added or updated
+   * in the store, we recommended using `store.push` over `store.pushPayload` after normalizing
+   * the payload directly. This can even take advantage of an existing serializer for the format
+   * the data is in, for example:
+   *
+   * ```js
+   * function pushPayload(store, modelName, rawPayload) {
+   *   const ModelClass = store.modelFor(modelName);
+   *   const serializer = store.serializerFor(modelName);
+   *   const jsonApiPayload = serializer.normalizeResponse(store, ModelClass, rawPayload, null, 'query');
+   *
+   *   return store.push(jsonApiPayload);
+   * }
+   * ```
+   *
+   * @method pushPayload [OPTIONAL]
+   * @public
+   * @optional
+   * @param {Store} store - the store service that initiated the request being normalized
+   * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request.
+   *  This JSON should be in the API format expected by the serializer.
+   * @returns {JsonApiDocument} - a document following the structure of a [JSON:API Document](https://jsonapi.org/format/#document-structure)
+   */
+  pushPayload?(store: Store, rawPayload: JSONObject): JsonApiDocument;
+}
+
+export default Serializer;
diff --git a/packages/store/addon/-private/ts-interfaces/utils.ts b/packages/store/addon/-private/ts-interfaces/utils.ts
index 3c6af61e96a..43d92465412 100644
--- a/packages/store/addon/-private/ts-interfaces/utils.ts
+++ b/packages/store/addon/-private/ts-interfaces/utils.ts
@@ -2,4 +2,5 @@
   @module @ember-data/store
 */
 
-export type Dict<K extends string, V> = { [KK in K]: V };
+export type ConfidentDict<V> = { [key: string]: V };
+export type Dict<V> = { [key: string]: V | undefined };