Reputation: 31
So I am trying to extend the object class. The reason why is because I want to write a function called isEqual
isEqual(object: any): boolean {
return JSON.stringify(this) === JSON.stringify(object);
}
I tried to extend Object, but there is no generic type.
I know I could just use a function that takes two arguments and checks it. But I want to learn how to do this in Typescript.
I read on StackOverflow that operator overloading is not possible (because originally I want to use the == operator to call the function above).
Could someone help me?
Upvotes: 2
Views: 1366
Reputation: 329418
You are looking to add an isEqual()
method to all instances of the global Object
type. In JavaScript this can be done by adding the method to Object.prototype
and all instances of objects will inherit the method automatically.
Before I say how to do it, it is almost universally considered a bad idea to modify native prototypes like that. See Why is extending native objects a bad practice? for the official SO answer about that. But briefly: Think of Object.prototype
as common property that needs to be shared by everyone. If you start making changes to it, and other code doesn't expect those changes, then that code can fail to run properly. Conversely, some other code could potentially make changes that make your changes fail to run properly.
The naïve way of adding a method to Object.prototype
, like Object.prototype.isEqual = ...
or Object.defineProperty(Object.prototype, 'isEqual', {value(...){...}})
will end up putting isEqual
as an enumerable property of all objects, meaning that a for...in
loop would suddenly show "isEqual"
as a key for every possible object everywhere.
The official advice here is don't do it.
If you still want to do it (e.g., maybe you are doing this with code nobody else will need to interact with, so the only person potentially harmed by this is you), then here's how. TypeScript supports declaration merging to allow additions to existing interfaces:
interface Object {
isEqual(object: object): boolean;
}
If you write that, the compiler will now expect that all Object
s have an isEqual()
method. (If your code is in a module, you will need to wrap that in a declare global { }
block, known as global augmentation).
Of course you still have to implement it:
Object.defineProperty(Object.prototype, 'isEqual', {
value(object: object) {
return JSON.stringify(this) === JSON.stringify(object);
},
configurable: true,
enumerable: false // <-- we don't want to mess with for...in loops
});
Here I've used the Object.defineProperty()
method so that we can explicitly prevent the method name from being enumerable.
And now you can verify that it works as desired:
interface Foo { a: number, b: string };
const o1: Foo = { a: 1, b: "two" };
const o2: Foo = { b: "two", a: 1 };
console.log(o1.isEqual(o2)); // false
The o1.isEqual(o2)
compiles with no error, and when you run it, it uses your isEqual()
method, and returns false
as expected. (You did expect false
, right? See sort object properties and JSON.stringify if you didn't.)
Upvotes: 4