Home > Back-end >  How to add a getter (extend) to the global Object or Array in TypeScript
How to add a getter (extend) to the global Object or Array in TypeScript

Time:12-20

I'm trying to add, for example a isEmpty getter to the Object or Array, but I'm having trouble to do so because I think I'm not really understanding how to access properties through this:

declare global {
  interface Object {
    isEmpty: boolean;
  }
}

// 1. Doesn't work
Object.prototype.isEmpty = this.keys.length === 0;

// 2. Doesn't work either
Object.prototype.isEmpty = Object.keys(this).length === 0;

Is it really possible to make this work? How would it be with Array?

References:

CodePudding user response:

⚠ WARNING! ⚠

IT IS WIDELY CONSIDERED BAD PRACTICE TO MODIFY NATIVE PROTOTYPES, mainly because it might change the code — specially its behavior — other programmers depend on.

See Why is extending native objects a bad practice? for more information. In what follows I'm going to show how to do it, but this is not meant to say that you should do it. A standalone isEmpty() function would work just as well, and is far less controversial.

Do check out @Dimava's answer as well, as he shows yet another way of doing this.


If you want to append a getter to an existing object, then you need to use the Object.defineProperty() method.

Your example could therefore look like:

// declare global { // <-- needed within modules
interface Object {
  isEmpty: boolean;
}
// }

Object.defineProperty(Object.prototype, "isEmpty", {
  configurable: true, get() { return Object.keys(this).length === 0 }
});

And you can verify that it works as desired:

const a = { z: 1 };
console.log(a.isEmpty) // false

const b = {};
console.log(b.isEmpty) // true

Playground link to code

CodePudding user response:

In this way it's easy to make extensions, and you don't have to worry about correct descriptors (making them non-enumerable, etc) and correct typing

class ObjectExtension extends Object {
    get isEmpty() {
        return Object.keys(this).length === 0;
    }
}

function extendSuperclassPrototype(classExtension: new (...a: any[]) => any): void {
    let desc = Object.getOwnPropertyDescriptors(classExtension.prototype);
    delete (desc as any).constructor;
    let superproto = Object.getPrototypeOf(classExtension).prototype;
    Object.defineProperties(Object.prototype, desc);
}

declare global {
    interface Object {
        isEmpty: ObjectExtension['isEmpty']
    }
}

extendSuperclassPrototype(ObjectExtension)

console.log(({a: 1}).isEmpty, ({}).isEmpty)
  • Related