diff --git a/src/annotation.js b/src/annotation.js
index 070b3f044..ce726998f 100644
--- a/src/annotation.js
+++ b/src/annotation.js
@@ -1,11 +1,12 @@
 import {Chart} from 'chart.js';
-import {clipArea, unclipArea, isObject, isArray} from 'chart.js/helpers';
+import {clipArea, isArray, isObject, unclipArea} from 'chart.js/helpers';
+import {version} from '../package.json';
+import {commonDefaults} from './commonDefaults';
+import {resolveType, updateElements} from './elements';
 import {handleEvent, hooks, updateListeners} from './events';
+import {requireVersion} from './helpers';
 import {adjustScaleRange, verifyScaleOptions} from './scale';
-import {updateElements, resolveType} from './elements';
 import {annotationTypes} from './types';
-import {requireVersion} from './helpers';
-import {version} from '../package.json';
 
 const chartStates = new Map();
 
@@ -113,11 +114,7 @@ export default {
       axis: undefined,
       intersect: undefined
     },
-    common: {
-      drawTime: 'afterDatasetsDraw',
-      label: {
-      }
-    }
+    common: commonDefaults
   },
 
   descriptors: {
diff --git a/src/commonDefaults.js b/src/commonDefaults.js
new file mode 100644
index 000000000..aab6f93b8
--- /dev/null
+++ b/src/commonDefaults.js
@@ -0,0 +1,14 @@
+export const commonDefaults = {
+  drawTime: 'afterDatasetsDraw',
+  label: {
+  },
+  xMax: undefined,
+  xMin: undefined,
+  xScaleID: undefined,
+  xValue: undefined,
+  yMax: undefined,
+  yMin: undefined,
+  yScaleID: undefined,
+  yValue: undefined,
+  z: 0
+};
diff --git a/src/elements.js b/src/elements.js
index 5d296f7ab..6ca76229e 100644
--- a/src/elements.js
+++ b/src/elements.js
@@ -1,6 +1,5 @@
 import {Animations} from 'chart.js';
-import {isObject, defined} from 'chart.js/helpers';
-import {hooks} from './events';
+import {defined, isObject} from 'chart.js/helpers';
 import {annotationTypes} from './types';
 
 const directUpdater = {
@@ -61,7 +60,7 @@ export function updateElements(chart, state, options, mode) {
       Object.assign(element, properties);
     }
 
-    properties.options = resolveAnnotationOptions(resolver);
+    properties.options = resolveOptions(resolver);
 
     animations.update(element, properties);
   }
@@ -86,7 +85,7 @@ function updateSubElements(mainElement, {elements, initProperties}, resolver, an
     const properties = definition.properties;
     const subElement = getOrCreateElement(subElements, i, definition.type, initProperties);
     const subResolver = resolver[definition.optionScope].override(definition);
-    properties.options = resolveAnnotationOptions(subResolver);
+    properties.options = resolveOptions(subResolver);
     animations.update(subElement, properties);
   }
 }
@@ -103,27 +102,15 @@ function getOrCreateElement(elements, index, type, initProperties) {
   return element;
 }
 
-function resolveAnnotationOptions(resolver) {
-  const elementClass = annotationTypes[resolveType(resolver.type)];
+function resolveOptions(resolver, path = []) {
   const result = {};
-  result.id = resolver.id;
-  result.type = resolver.type;
-  result.drawTime = resolver.drawTime;
-  Object.assign(result,
-    resolveObj(resolver, elementClass.defaults),
-    resolveObj(resolver, elementClass.defaultRoutes));
-  for (const hook of hooks) {
-    result[hook] = resolver[hook];
-  }
-  return result;
-}
-
-function resolveObj(resolver, defs) {
-  const result = {};
-  for (const prop of Object.keys(defs)) {
-    const optDefs = defs[prop];
-    const value = resolver[prop];
-    result[prop] = isObject(optDefs) ? resolveObj(value, optDefs) : value;
+  for (const key of Object.getOwnPropertyNames(resolver)) {
+    if (path.includes(key)) {
+      // TODO: this is slow, should figure out why the keys start looping instead.
+      continue;
+    }
+    const value = resolver[key];
+    result[key] = isObject(value) ? resolveOptions(value, [...path, key]) : value;
   }
   return result;
 }
diff --git a/src/types/box.js b/src/types/box.js
index 32a726e74..32f987911 100644
--- a/src/types/box.js
+++ b/src/types/box.js
@@ -97,13 +97,6 @@ BoxAnnotation.defaults = {
   shadowBlur: 0,
   shadowOffsetX: 0,
   shadowOffsetY: 0,
-  xMax: undefined,
-  xMin: undefined,
-  xScaleID: undefined,
-  yMax: undefined,
-  yMin: undefined,
-  yScaleID: undefined,
-  z: 0
 };
 
 BoxAnnotation.defaultRoutes = {
diff --git a/src/types/ellipse.js b/src/types/ellipse.js
index e6a4783fb..78b104c10 100644
--- a/src/types/ellipse.js
+++ b/src/types/ellipse.js
@@ -59,13 +59,6 @@ EllipseAnnotation.defaults = {
   shadowBlur: 0,
   shadowOffsetX: 0,
   shadowOffsetY: 0,
-  xMax: undefined,
-  xMin: undefined,
-  xScaleID: undefined,
-  yMax: undefined,
-  yMin: undefined,
-  yScaleID: undefined,
-  z: 0
 };
 
 EllipseAnnotation.defaultRoutes = {
diff --git a/src/types/label.js b/src/types/label.js
index 169f43d2e..487eb80c2 100644
--- a/src/types/label.js
+++ b/src/types/label.js
@@ -102,16 +102,7 @@ LabelAnnotation.defaults = {
   textStrokeWidth: 0,
   width: undefined,
   xAdjust: 0,
-  xMax: undefined,
-  xMin: undefined,
-  xScaleID: undefined,
-  xValue: undefined,
   yAdjust: 0,
-  yMax: undefined,
-  yMin: undefined,
-  yScaleID: undefined,
-  yValue: undefined,
-  z: 0
 };
 
 LabelAnnotation.defaultRoutes = {
diff --git a/src/types/point.js b/src/types/point.js
index 97c0dd039..2bedb92bd 100644
--- a/src/types/point.js
+++ b/src/types/point.js
@@ -61,16 +61,7 @@ PointAnnotation.defaults = {
   shadowOffsetX: 0,
   shadowOffsetY: 0,
   xAdjust: 0,
-  xMax: undefined,
-  xMin: undefined,
-  xScaleID: undefined,
-  xValue: undefined,
   yAdjust: 0,
-  yMax: undefined,
-  yMin: undefined,
-  yScaleID: undefined,
-  yValue: undefined,
-  z: 0
 };
 
 PointAnnotation.defaultRoutes = {
diff --git a/src/types/polygon.js b/src/types/polygon.js
index bcf08e92f..4fd260b2a 100644
--- a/src/types/polygon.js
+++ b/src/types/polygon.js
@@ -83,16 +83,7 @@ PolygonAnnotation.defaults = {
   shadowOffsetY: 0,
   sides: 3,
   xAdjust: 0,
-  xMax: undefined,
-  xMin: undefined,
-  xScaleID: undefined,
-  xValue: undefined,
   yAdjust: 0,
-  yMax: undefined,
-  yMin: undefined,
-  yScaleID: undefined,
-  yValue: undefined,
-  z: 0
 };
 
 PolygonAnnotation.defaultRoutes = {