TL; DR; Расширение из массива, создание частного конструктора и создание экземпляра с использованием фабрики:

Почему:

TypeScript часто называют JavaScript для разработчиков C #. Это похоже на C #, но не заблуждайтесь, вы все еще находитесь в стране JavaScript.

Давайте перейдем к делу: вы привыкли к методам ООП и теперь хотите создать объект, подобный массиву, с помощью собственных пользовательских методов. Справедливо, давайте попробуем просто расширить Array:

Он компилируется, и вы счастливы, потому что он будет работать, не так ли? На самом деле, если вы запустите это, вы получите:

TypeError: myArray.logCount не является функцией

В этот момент вы, вероятно, почесываете голову (это такая банальная вещь, и она компилируется!). Проблема в том, что TypeScript не компилируется в IL .Net, он компилируется в JavaScript… а расширение Arrays - большой запрет в JavaScript.

Проблема в том, что при расширении Array во время выполнения вы не получаете объект с прототипом, который вы написали. Вместо этого вы получаете объект с прототипом Array. Почему? Это JavaScript.

Одно из решений - заменить прототип объекта во время выполнения:

Теперь все работает как положено. Вы запускаете это и получаете:

Количество: 0

Это работает, потому что вы заменяете «плохой» прототип в своем объекте (массиве) на «хороший» прототип объекта, который вы определили.

У этого решения есть две проблемы.

Во-первых, он медленнее, потому что движки JS не оптимизируются для прототипов, измененных во время выполнения. Честно говоря, я не особо беспокоюсь об этом, потому что он все еще чертовски быстр. У меня нет проблем с тем, что я могу создавать «всего» несколько сотен тысяч таких объектов в секунду.

Самый большой из них заключается в том, что он нарушает наследование. JavaScript использует прототипное наследование: у объекта есть прототип / чертеж, сам прототип является объектом, поэтому он также имеет прототип, повторяйте, пока прототип не станет нулевым и это не конец цепочки.

Учтите следующее:

Я не хочу, чтобы будущий разработчик столкнулся с этой проблемой во время выполнения, и я, конечно же, не хочу, чтобы они тратили циклы, пытаясь выяснить первопричину ошибки.

Хорошим решением было бы запечатать класс A, чтобы предотвратить его расширение будущими разработчиками, за исключением того, что TypeScript не поддерживает классы запечатывания (и на момент написания этой статьи команда TS находится в лагере «не произойдет»).

Так что давайте обойдем это. Один из вариантов - вообще забыть о расширении массива и просто создать простой объект со свойством массива и добавить методы, которые взаимодействуют с массивом:

Количество: 1

Это работает, но вы, вероятно, пришли сюда не для того, чтобы вам сказали не расширять массив. Способ расширения от Array, не позволяющий будущим разработчикам совершить ошибку расширения класса (во время компиляции), - это создать частный конструктор и создать его экземпляр с помощью фабрики:

Теперь кто-то может сказать: «Мне это не нужно. Я просто должен помнить, что не следует расширять этот конкретный класс, или не забывать добавлять дополнительный конструктор в расширяющийся класс ». Мой ответ прост: я использую TypeScript, чтобы уменьшить количество ошибок во время выполнения.

Удачного кодирования.

PS. Если вы используете Angular и ненавидите писать шаблонный код, попробуйте SimonTest. Он берет на себя самую утомительную часть тестирования компонентов и сервисов: имитацию зависимостей.