2

I am doing some experimentation with Typescript and I am trying to add a property to the Object prototype so that it is available to all objects in all of my modules.

Here is what I have so far:

In a Common.ts file

Object.defineProperty(Object.prototype, 'notNull', {
    value: function(name: string){
        if(this === null || this === undefined){
            throw new Error(`${name} cannot be null nor undefined`);
        }

        return this;
    },
    enumerable: false
});

Now I would like to use it in another file like so:

module SomeModule{
    class Engine{
        constructor(public horsePower: number, public engineType: string){}
    }

    class Car{
        private _engine: Engine;

        constructor(private engine: Engine){
            //Of course here the compiler complains about notNull not existing
            this._engine = engine.notNull('engine');
        }
    }
}

Now I am at a loss since I am not sure that by exporting "Object" with module.exports in Common.ts makes sense at all. And even if I do that and import it on my other file, that does not seem to do anything.

Is there a way to implement something like this?

Thank you.

Sergio Romero
  • 6,477
  • 11
  • 41
  • 71
  • Why would you want to do this? Why not just create a utility function? Polluting Object.prototype is [risky business](https://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/) because it can easily become messy and bug-prone since those functions will be available to any POJO in your system, even including things Typescript uses. – nem035 May 04 '17 at 18:11
  • @nem035 - Like I said I am experimenting and this was a straight forward example to figure things out and understand how something like this might work. – Sergio Romero May 04 '17 at 18:16
  • ok, cool, curiousness is how we learn :). I think Nitzan's answer is on the right track – nem035 May 04 '17 at 18:17
  • 2
    `notNull` is absolutely pointless, btw. Neither `null` nor `undefined` are objects or inherit from `Object.prototype`. All you'll get is a ReferenceError – Bergi May 04 '17 at 18:25
  • @Bergi - You are absolutely right, this method is useless and not fulfilling its purpose, I guess I am still thinking too much in C# and wanted to do some kind of extension method which of course cannot be done with Typescript. That being said, with Nitzan's help I am very close to solving the problem and thus learning how to do this, which is the main objective. So in a way it did fulfill its purpose :) – Sergio Romero May 04 '17 at 20:07

1 Answers1

4

When you change the Object.prototype it will affect everything in the environment that is running your code, which is why you'd usually be advised to avoid extending native types.

With that being said, if you do want to walk this path, then here's what you need to do:
As I said, once you change the prototype it will already be available at runtime, but the compiler isn't yet aware of the change so it will complain when you try to use this new method.

To get around that, you need to use global augmentation:

declare global {
    interface Object {
        notNull(name: string): this;  
    }
}

Edit

As the comment to this answer suggested, this approach only works if you're using modules (import/export), but if you don't then you need to do this:

interface Object {
    notNull(name: string): this;  
}

And put it in a place that is used in any file in which you're trying to use notNull, for example in a .d.ts file that is referenced in all your ts source files.


2nd Edit

To use modules you'll just do:

export class Engine{
    constructor(public horsePower: number, public engineType: string){}
}

export class Car {
    private _engine: Engine;

    constructor(private engine: Engine){
        //Of course here the compiler complains about notNull not existing
        this._engine = engine.notNull('engine');
    }
}

Without sourounding it with module SomeModule { ... }

Community
  • 1
  • 1
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • He does not appear to be using modules in his example so it would be good to mention what `declare global` does and that it is only for use inside a module. – Aluan Haddad May 04 '17 at 19:09
  • 1
    @AluanHaddad You're right. I haven't noticed he's not importing/exporting. Revised my answer. Thanks. – Nitzan Tomer May 04 '17 at 19:27
  • @NitzanTomer - Thank you. I don't think I am quite able to follow the solution. I'm fine using modules but how exactly should this be put together? Would you mind elaborating the example with modules? – Sergio Romero May 04 '17 at 19:32
  • Are you asking about modules in general? If so then you might want to first start with these two docs: [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html) and [Modules](https://www.typescriptlang.org/docs/handbook/modules.html). Or are you asking specifically about your case? I'd love to explain, just trying to understand what you're having trouble with – Nitzan Tomer May 04 '17 at 19:34
  • @NitzanTomer - I mean specifically about this case. What would the complete solution look like? I've already got it working without modules but since this is a learning effort I'd like to understand the other option as well. Thank you. – Sergio Romero May 04 '17 at 20:18
  • Check my revised answer – Nitzan Tomer May 04 '17 at 20:46