3

I have several polyfills, which I am using in my solution. For example, Array.includes:

 if (![].includes) {
  Array.prototype.includes = function(searchElement/*, fromIndex*/) {
    'use strict';
    var O = Object(this);
    var len = parseInt(O.length) || 0;
    if (len === 0) {
      return false;
    }
    var n = parseInt(arguments[1]) || 0;
    var k;
    if (n >= 0) {
      k = n;
    } else {
      k = len + n;
      if (k < 0) {
        k = 0;
      }
    }
    while (k < len) {
      var currentElement = O[k];
      if (searchElement === currentElement ||
         (searchElement !== searchElement && currentElement !== currentElement)
      ) {
        return true;
      }
      k++;
    }
    return false;
  };
}

But I want to migrate my utils.js to utils.ts (I never user TS so I want to play with it), and I write following:

interface Array<T> {
    includes(searchElement: T, fromIndex?: number): boolean;
}

if (!Array.prototype.hasOwnProperty('includes')) {
    class MyArray<T> extends Array<T> {
        includes(searchElement: T, fromIndex?: number): boolean {
            const obj = Object(this);
            const len = parseInt(obj.length) || 0;
            if (len === 0) {
                return false;
            }
            const n = fromIndex || 0;
            var k: number;
            if (n >= 0) {
                k = n;
            } else {
                k = len + n;
                if (k < 0) {
                    k = 0;
                }
            }
            while (k < len) {
                const currentElement = obj[k];
                if (searchElement === currentElement ||
                        (searchElement !== searchElement && currentElement !== currentElement)
                ) {
                    return true;
                }
                k++;
            }
            return false;
        }
    }
}

Array.prototype.includes still undefined in IE. Of course, because TS is extending my custom class instead of native Array:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
if (!Array.prototype.hasOwnProperty('includes')) {
    var MyArray = (function (_super) {
        __extends(MyArray, _super);
        function MyArray() {
            _super.apply(this, arguments);
        }
        MyArray.prototype.includes = function (searchElement, fromIndex) {
            var obj = Object(this);
            var len = parseInt(obj.length) || 0;
            if (len === 0) {
                return false;
            }
            var n = fromIndex || 0;
            var k;
            if (n >= 0) {
                k = n;
            }
            else {
                k = len + n;
                if (k < 0) {
                    k = 0;
                }
            }
            while (k < len) {
                var currentElement = obj[k];
                if (searchElement === currentElement ||
                    (searchElement !== searchElement && currentElement !== currentElement)) {
                    return true;
                }
                k++;
            }
            return false;
        };
        return MyArray;
    }(Array));
}

How do I extend Array itself?

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • 1
    Possible duplicate of [TypeScript: augmenting built-in types](http://stackoverflow.com/questions/12701732/typescript-augmenting-built-in-types) – Heretic Monkey Dec 02 '16 at 18:49
  • @MikeMcCaughan thank you for a link. But one question leaves: for example [here](http://typescript.codeplex.com/workitem/4) is described `Array` extension but there is nothing about extending strongly-typed (I mean `Array` or something) – Alex Zhukovskiy Dec 02 '16 at 18:51
  • That's not what your question is about. Just do a search on your title (or the title of the duplicate) and you'll get plenty of hits. – Heretic Monkey Dec 02 '16 at 18:53
  • @MikeMcCaughan Well, it answers how to add my custom methods. But there is another question: how do I add polyfills? For example I support IE9 which doesn't have `Array.includes` method. So I only should add it if it doesn't currently exists. – Alex Zhukovskiy Dec 02 '16 at 18:58
  • Honestly, just do some research. All of this information is on the internet as is easily searchable. Hint: [MDN](https://developer.mozilla.org/) has polyfills for almost everything. A good polyfill should only define a method if it doesn't already exist. – Heretic Monkey Dec 02 '16 at 19:11
  • @MikeMcCaughan I know it, if you examine the code above you will see that I used `hasOwnProperty` to determine if property already exists. But it's possible because JS is interpreted line-by-line. And in `TS` I just declare methods in the interface, I have no option `declare under some conditions`. – Alex Zhukovskiy Dec 02 '16 at 19:17
  • @MikeMcCaughan I always google before asking a question. And I didn't find anything about `how to implement method in TS for it being available from JS as instance method call`. Everything I found is about how to create your own static class and call `MyCoolArray.includes(...)`, but nothing about extending native prototype to be able to write `[1,2,3].includes(...)` in both JS and TS. – Alex Zhukovskiy Dec 02 '16 at 19:25
  • @AlexZhukovskiy You never need to "declare under some conditions". If you want to use methods that are in the future standard, just use declarations from standard typescript library that conforms to that standard: use `--lib es2015`, or `--lib es2016`, or [whatever you need](https://www.typescriptlang.org/docs/handbook/compiler-options.html), and make sure appropriate polyfill is loaded at run time. If you want to extend standard class with your own methods that no one knows about, always declare them. – artem Dec 02 '16 at 19:36
  • There are no built-in types in typescript. `Array` is provided by javascript runtime, there is no way to extend it in Typescript other than using the very same methods that you use to extend it in javascript. Typescript library provides declarations only, it's used at compile time for typechecking, and that's all. All so-called 'built-in' types come from javascript runtime, typescript does not have its own runtime library. – artem Dec 02 '16 at 19:50
  • @artem I often see answers `it's not possible` but still hope that it's not true. I just want to add, for example, `format` method to string itself and use it from TS, knowing that this method returns string, but number or elephant. If it's possible, next step is writing polyfill, so I have strongly typed interface, and two implementations, native in case if this method implemented in browser and custom in case when it isn't. – Alex Zhukovskiy Dec 02 '16 at 19:54
  • 1
    `String` is nothing special. If you defined your own class in typescript, can you later add a new method to it, without modifying that class definition? The only way I know of is to declare that method using [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html), and then, separately, add implementation by using `defineProperty` or modifying its prototype. There is no built-in way to do it in one step in the language. – artem Dec 02 '16 at 20:01
  • @artem i'l piking your answer (regardless it was just a comment :) ). It is working: i'm extending prototype and still have strong type check – Alex Zhukovskiy Dec 03 '16 at 12:25

1 Answers1

1

Instead of class MyArray<T> extends Array<T> { do `Array.prototype.includes = /insert your function here/

More

You should also declare includes as a member of the Array if you need to by extending lib.d.ts

basarat
  • 261,912
  • 58
  • 460
  • 511