docs: function propertySort(itemsKey, sortPropertiesKey) {
    let activeObserversMap = new WeakMap();
    let sortPropertyDidChangeMap = new WeakMap();
  
    if (EMBER_METAL_TRACKED_PROPERTIES) {
      let cp = computed(`${itemsKey}.[]`, `${sortPropertiesKey}.[]`, function(key) {
        let sortProperties = get(this, sortPropertiesKey);
  
        assert(
          `The sort definition for '${key}' on ${this} must be a function or an array of strings`,
          isArray(sortProperties) && sortProperties.every(s => typeof s === 'string')
        );
  
        let itemsKeyIsAtThis = itemsKey === '@this';
        let normalizedSortProperties = normalizeSortProperties(sortProperties);
  
        let items = itemsKeyIsAtThis ? this : get(this, itemsKey);
        if (!isArray(items)) {
          return emberA();
        }
  
        if (normalizedSortProperties.length === 0) {
          return emberA(items.slice());
        } else {
          return sortByNormalizedSortProperties(items, normalizedSortProperties);
        }
      }).readOnly();
  
      descriptorForDecorator(cp).auto();
  
      return cp;
    } else {
      return computed(`${sortPropertiesKey}.[]`, function(key) {
        let sortProperties = get(this, sortPropertiesKey);
  
        assert(
          `The sort definition for '${key}' on ${this} must be a function or an array of strings`,
          isArray(sortProperties) && sortProperties.every(s => typeof s === 'string')
        );
  
        // Add/remove property observers as required.
        let activeObservers = activeObserversMap.get(this);
  
        if (!sortPropertyDidChangeMap.has(this)) {
          sortPropertyDidChangeMap.set(this, function() {
            notifyPropertyChange(this, key);
          });
        }
  
        let sortPropertyDidChange = sortPropertyDidChangeMap.get(this);
  
        if (activeObservers !== undefined) {
          activeObservers.forEach(path => removeObserver(this, path, sortPropertyDidChange));
        }
  
        let itemsKeyIsAtThis = itemsKey === '@this';
        let normalizedSortProperties = normalizeSortProperties(sortProperties);
        if (normalizedSortProperties.length === 0) {
          let path = itemsKeyIsAtThis ? `[]` : `${itemsKey}.[]`;
          addObserver(this, path, sortPropertyDidChange);
          activeObservers = [path];
        } else {
          activeObservers = normalizedSortProperties.map(([prop]) => {
            let path = itemsKeyIsAtThis ? `@each.${prop}` : `${itemsKey}.@each.${prop}`;
            addObserver(this, path, sortPropertyDidChange);
            return path;
          });
        }
  
        activeObserversMap.set(this, activeObservers);
  
        let items = itemsKeyIsAtThis ? this : get(this, itemsKey);
        if (!isArray(items)) {
          return emberA();
        }
  
        if (normalizedSortProperties.length === 0) {
          return emberA(items.slice());
        } else {
          return sortByNormalizedSortProperties(items, normalizedSortProperties);
        }
      }).readOnly();
    }
  }