Extending array fails in JavaScript ES6 - javascript

I've some problems to extend an JS array. These are my classes:
// ArrayList.js
export default class ArrayList extends Array {
clear() {
this.splice(0, this.length);
}
}
// MyItem.js
export default class MyItem {
constructor() {
this._id = 0;
}
getID() {
return this._id;
}
setID(value) {
if (typeof value === 'number') {
this._id = value;
}
}
}
// Test.js
import ArrayList from './ArrayList';
import MyItem from './MyItem';
let list = new ArrayList();
let item1 = new MyItem();
item1.setID(1);
list.push(item1);
let item2 = new MyItem();
item2.setID(2);
list.push(item2);
If I now execute:
list.forEach(function(item) {
console.log(item.getID());
});
Everything works perfect but if I try to call my custom method I run into errors:
list.clear();
console.log(list.length);
The exception is:
TypeError: list.clear is not a function
+++ UPDATE +++
I use the test script with node.js:
node start.js
That's my start.js:
require('babel-register')({
presets: [ 'env' ]
})
module.exports = require('./Test.js')
And then every class is stored in a separate JS file.

I don't like your import end exports. Try modules (https://nodejs.org/api/modules.html) and this should work without Babel in Node.
module.exports = class MyItem {
// class content
}
module.exports = class ArrayList extends Array {
clear() {
this.splice(0, this.length);
}
}
// in the test file
const ArrayList = require('./ArrayList');
const MyItem = require('./MyItem');

Related

Javascript - How to access class methods within a return function

I've been stuck on this issue for a while. I cannot describe it accurately enough to find solutions online - apologies if it is a duplicate question.
I want to access helloWorld() from module.js:
export function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
In another file:
import { HelperProvider } from 'module.js'
const helperProvider = HelperProvider;
const helper = new helperProvider();
helper.helloWorld();
However, I encounter the following error:
Uncaught TypeError: helper.helloWorld is not a function
Any help would be very much appreciated.
You need to invoke the function HelperProvider to get the class.
const helperProvider = HelperProvider();
function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();
You are using module features that's not out of the box in nodejs, if you want to use modules you'll need to set type: "module" in the package.json file... see details
If you wanna use node ways:
module.js
function HelperProvider() {
return class Helper {
constructor() {}
helloWorld() {
console.log("Hello World");
}
};
}
module.exports = HelperProvider;
index.js
const HelperProvider = require("./Helper");
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();

How to inform TypeScript compiler of extensions to JS Array prototype?

I have created a polyfill for the JavaScript Array;
if (Array.prototype.remove !== 'function') {
Array.prototype.remove = function (value) {
var idx = this.indexOf(value);
if (idx !== -1) {
return this.splice(idx, 1);
}
return false;
};
}
Now I am upgrading the original JavaScript project to a TypeScript project and the tsc complains about the usage of the .remove method:
class Archive {
documents: DocInfo[] = []; // <-- Array of class DocInfo
addDocument(document: DocInfo) {
...
}
deleteDocument(document: DocInfo) {
this.documents.remove(document);
^^^^^^
tsc complains here: TS2339:Property 'remove' does not exist on type 'DocInfo[]'
}
}
How can I tell the tsc about this extension?
I tried creating a typings file, but without any success:
declare module 'Array' {
export function removeByAttr(propertyName: string, propertyValue: any);
}
Thanks
The typings should extend Array<T> interface:
interface Array<T> {
remove(item: T): boolean;
}
Extending the Array class with an interface it's simple, you could try something like this:
Playground
interface Array<T> {
remove(o: T): Array<T>;
}
Array.prototype.remove = function (o) {
var idx = this.indexOf(o);
if (idx !== -1) {
return this.splice(idx, 1);
}
return this;
}
class DocInfo {
name: string ;
constructor(name) {
this.name = name;
}
}
class Archive {
documents: DocInfo[] = [];
addDocument(document: DocInfo) {
this.documents.push(document);
}
deleteDocument(document: DocInfo) {
this.documents.remove(document);
}
printDocuments() {
this.documents.forEach((item: DocInfo) => {
console.log(item.name);
});
}
}
const a = new Archive();
const _1 = new DocInfo('1');
const _2 = new DocInfo('2');
a.addDocument(_1);
a.addDocument(_2);
a.printDocuments();
a.deleteDocument(_1);
console.log('*********************');
a.printDocuments();
console.log('*********************');
a.addDocument(_1);
a.deleteDocument(_2);
a.printDocuments();

TypeScript Class Being Used In Cast Not Loading

I've created a model in TypeScript that I'm using in a cast. When running the application, the model is not loaded and I'm unable to use any functions on that model.
Model
export class DataIDElement extends HTMLElement {
get dataID(): number {
var attributes: NamedNodeMap = this.attributes;
var dataIDAttribute: Attr = attributes.getNamedItem("data-id");
if (!dataIDAttribute) {
//throw error
}
var value: number = Number(dataIDAttribute.value);
return value;
}
}
Angular Component (Where model is being imported)
import { DataIDElement } from '../../models/dataIdElement';
export class PersonComponent
{
personClicked(event: KeyboardEvent): void {
var element: DataIDElement = <DataIDElement>event.target;
// This code always returns undefined (model isn't loaded)
var personID: number = element.dataID;
}
}
What you are doing there is a type assertion. That only overwrites the type inference of the compiler to make it believe that event.target is of the type DataIDElement. It doesn't create a new instance of DataIDElement.
If you want to create an instance of DataIDElement you need to create it using new.
DataIDElement would look something like this:
export class DataIDElement extends HTMLElement {
constructor(private target: HTMLElement) {}
get dataID(): number {
var attributes: NamedNodeMap = this.target.attributes;
var dataIDAttribute: Attr = attributes.getNamedItem("data-id");
if (!dataIDAttribute) {
//throw error
}
var value: number = Number(dataIDAttribute.value);
return value;
}
}
And would be used like this:
import { DataIDElement } from '../../models/dataIdElement';
export class PersonComponent
{
personClicked(event: KeyboardEvent): void {
var element: DataIDElement = new DataIDElement(event.target);
// This code always returns undefined (model isn't loaded)
var personID: number = element.dataID;
}
}

Convert Namespaced Javascript Functions to Typescript

I know that all javascript is valid typescript but I'd like to start converting my javascript to typescript conventions. I'm battling this one snippet of JS:
My standard Javascript that works
if (MyCompany === undefined) {
var MyCompany = {};
}
MyCompany.Uploader = MyCompany.Uploader || {};
MyCompany.Uploader.Core = function (config) {
'use strict';
function build() {
console.log("building");
}
return {
build: build
};
};
var config = {this: "that};
MyCompany.Uploader.Core(config).build(); // outputs building to console
I've been messing with multiple approaches and I feel like I not close enough.
My failed attempt at converting to Typescript
namespace MyCompany.Uploader {
export var Core = (config:any) => {
function build() {
console.log("building");
}
};
}
let configobj = {here:"there"};
MyCompany.Uploader.Core(configobj).build();
This simply doesn't work. I can't seem to access the build function. I'm sure this is a rookie mistake.
The error I get: Property build does not exist on type void
That's because you did not add an important part of your javascript code into the typescript version, and that's the return object which contains the reference for the build function, it should be:
namespace MyCompany.Uploader {
export var Core = (config: any) {
function build() {
console.log("building");
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();
You can also define interfaces for the config and the return object:
namespace MyCompany.Uploader {
export interface Config {
here: string;
}
export interface Builder {
build: () => void;
}
export var Core = (config: Config): Builder => {
function build() {
console.log(config.here);
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();

ES6 / Meteor Singleton pattern with inheritance

I have programmed a singleton using this blog:
http://amanvirk.me/singleton-classes-in-es6/
But I need a singleton on both client and server side.
That is, I have three programs: common.js
export class AppsManagerCommon {
constructor(options) {
// general options
this.options = options;
// list of registered apps
this.apps = [];
}
registerApp(app) {
a = this.apps.find(function (a) {return a.name === app.name;});
console.log(a);
this.apps.push(app);
}
}
client.js
import { AppsManagerCommon } from 'common.js';
let instanceAppsManager = null; // singleton pattern
export class AppsManager extends AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
super(options);
instanceAppsManager = this;
}
return instanceAppsManager;
}
}
server.js (identical to client.js)
import { AppsManagerCommon } from 'common.js';
let instanceAppsManager = null; // singleton pattern
export class AppsManager extends AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
super(options);
instanceAppsManager = this;
}
return instanceAppsManager;
}
}
The singletoc can be used sucessfully with:
a = new AppsManager();
a.registerApp({name:'app1'});
but as soon as I do
b = new AppsManager(); // should be the same instance
I get an error:
ReferenceError: this hasn't been initialised - super() hasn't been called
at BabelRuntime.possibleConstructorReturn (packages/babel-runtime.js:206:13)
I can more or less understand what the error means, but I have no clue how I could resolve the issue.
EDIT 1
NB The existence check in registerApp does not work, but is not a problem for now
This is a working solution:
common.js
let instanceAppsManager = null; // singleton pattern
export class AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
instanceAppsManager = this;
// general options
this.options = options;
// list of registered apps
this.apps = [];
}
return instanceAppsManager;
}
registerApp(app) {
this.apps.push(app);
}
}
server.js (identical to client.js)
import { AppCommon, AppsManagerCommon } from 'common.js';
export class AppsManager extends AppsManagerCommon {
constructor(options) {
super(options);
}
}

Categories