4

I need the ability to easily sort collections and I've chosen to extend the Array primitive. I realize this is considered bad practice in most cases, but this will be used only internally (not a shared lib). No matter what approach I've tried I'm not getting a working result even though it works as expected in the playground.

I've tried adding the polyfill inline in /src/polyfills.ts but that gives me a "TypeError: Attempted to assign to readonly property" in the console when I call the $sortBy method...

declare global {
  interface Array<T> {
    $sortBy(sortKey:string): T[];
  }
}

if (!Array.prototype['$sortBy']) {

  Object.defineProperty(Array.prototype, '$sortBy', {
    value: function(sortKey) {
      return this.sort( function(a, b) { // TypeError here???
        if (a[sortKey] < b[sortKey]) { return -1; }
        if (a[sortKey] > b[sortKey]) { return 1; }
        return 0;
      });
    }
  })

}

I've also tried adding a plain javascript version via npm and importing but that gives me the same type error. What's the secret???

/node_modules/my-polyfills/sortBy.js

if (!Array.prototype.$sortBy) {
  Object.defineProperties(Array.prototype, {
    '$sortBy': {
      value: function (sortKey) {
        return this.sort(function (a, b) {
          if (a[sortKey] < b[sortKey]) {
            return -1;
          }
          if (a[sortKey] > b[sortKey]) {
            return 1;
          }
          return 0;
        });
      }
    }
  });
}

.angular-cli.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": { ... },
  "apps": [
    {
      ...,
      "scripts": [
        "../node_modules/my-polyfills/sortBy.js",
        "../node_modules/moment/moment.js"
      ],
      ...
    }
  ],
  ...
}
Brian
  • 1,363
  • 3
  • 12
  • 26
  • 1
    have you tried: http://stackoverflow.com/questions/37640785/how-do-you-add-polyfills-to-globals-in-typescript-modules ? – Ahmed Musallam Apr 01 '17 at 05:25
  • Thanks @AhmedMusallam! That fixed my intellisense error. I've updated my example to include `declare global`. However, the type error still remains :( – Brian Apr 01 '17 at 16:01
  • are you using **both** the `/node_modules/my-polyfills/sortBy.js` file And the first file which I assume is TS? this error `TypeError: Attempted to assign to readonly property` happens when you try to assign a non-writable property to something. In this case if you define `Arrays.prototype.$sortBy` THEN you try to modify that `$sortBy` by assigning it to a new function, you'll get that error. I ask because when I try your code in the browser, the first snippet, it works just fine. – Ahmed Musallam Apr 02 '17 at 04:15

1 Answers1

4

I have an generated app on angular cli 1.0.0. While I think your issue has nothing to do with the version, I have taken your code below and appended it to src/polyfills.ts it works as expected:

declare global {
  interface Array<T> {
    $sortBy(sortKey:string): T[];
  }
}

if (!Array.prototype['$sortBy']) {

  Object.defineProperty(Array.prototype, '$sortBy', {
    value: function(sortKey) {
      return this.sort( function(a, b) { // TypeError here???
        if (a[sortKey] < b[sortKey]) { return -1; }
        if (a[sortKey] > b[sortKey]) { return 1; }
        return 0;
      });
    }
  })
}

in one of my components I added:

var a = [1,2,3];
var b = a.$sortBy('');
console.log(b)

I did not see the error in the console and array a was printed just fine.

I think the issue you are having is because you have the code above in your src/polyfills.ts AND you have included the same polyfill in /node_modules/my-polyfills/sortBy.js and added that to scripts section of your .angular-cli

You should add either or, not both. I recommend the former, but in it's own file and not appended to polyfills.ts

this error TypeError: Attempted to assign to readonly property happens when you try to assign a non-writable property to something else. By using Object.defineProperty you are making $sortBy non-writable. You defined Arrays.prototype.$sortBy THEN you try to modify that $sortBy by assigning it to a new function, hence you get the error.

Ahmed Musallam
  • 9,523
  • 4
  • 27
  • 47