5

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. I keep getting the error: TypeError: Attempted to assign to readonly property. This is a simplified example of my latest iteration. I have a more complete version of this working in the playground so I'm assuming he problem lies with the angular-cli config/build process???

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 this approach but my IDE (VSCode) gives me the error: [ts] Property '$sortBy' does not exist on type 'any[]'.

         Intellisense error here
                   |
                   V
Array.prototype.$sortBy = function(sortKey:string){
  return this.sort( function(a, b) {
    ...
  });
}

tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "lib": [
      "es2016"
    ]
  }
}

tsconfig.app.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
  "lib": [
    "es2016",
    "dom"
  ],
    "outDir": "../out-tsc/app",
    "target": "es5",
    "module": "es2015",
    "baseUrl": "",
    "types": []
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ]
}
Brian
  • 1,363
  • 3
  • 12
  • 26
  • Which version of typescript are you using? What's your `tsconfig.json`? Your code works fine in playground. – Nitzan Tomer Mar 21 '17 at 21:27
  • Maybe https://github.com/basarat/typescript-collections has something that would suit your needs better. I haven't tried the array utilities but the data structures are nice. – Mitch Talmadge Mar 21 '17 at 21:35
  • Checkout: http://stackoverflow.com/questions/14000645/how-to-extend-native-javascript-array-in-typescript Starting in TypeScript 1.6, you can extend the Array type – Bas van Dijk Mar 21 '17 at 21:36
  • @NitzanTomer added my ts configs above. I'm using TS 2.1.6. This is an angular-cli project – Brian Mar 21 '17 at 21:37
  • I can't reproduce your problem. It works fine for me – Nitzan Tomer Mar 21 '17 at 21:46
  • 1
    Also, regardless of your problem, extending of the `Array` interface needs to be outside of the `if (!Array.prototype['$sortBy'])` – Nitzan Tomer Mar 21 '17 at 21:48
  • Thanks @osi but it looks like that approach causes problems... http://stackoverflow.com/questions/33947854/class-extended-from-built-in-array-in-typescript-1-6-2-does-not-update-length-wh – Brian Mar 21 '17 at 23:50
  • Thanks @NitzanTomer I updated my example. Still can't figure out what the problem is. I'm assuming it's a problem with my environment. – Brian Mar 21 '17 at 23:53
  • Your 2nd approach (`Array.prototype.$sortBy = ...`) indeed fails if you extend the `Array` interface inside the `if`, but when it's above it, then the compilation error goes away. – Nitzan Tomer Mar 21 '17 at 23:57

1 Answers1

1

I had this problem as well, so just pasting my results here in case someone needs.

in your main.ts file add this:

// Here will be all extension methods that will be shared globally across the app
declare global {
  interface Array<T> {
    sortAlphaNumeric(fieldName: string): Array<T>;
  }
}

then you can create a file with this:

    /** 
     * The extension of Array type allows to sort AlphaNumeric data
     * @param fieldName {string} requires the field name which you want to sort by
     */
    Array.prototype.sortAlphaNumeric = function (fieldName: string) {
        return this.sort((a, b) => {
            return a[fieldName].toLowerCase().localeCompare(b[fieldName].toLowerCase(), undefined, {
                numeric: true,
                sensitivity: 'base'
            });
        });
    };
loonix
  • 430
  • 7
  • 14