Question

Before I proceed with my question, allow me to point out that I have already done some considerable research into this topic, and have already asked a couple of related questions, which you can find here:

Extending Object.prototype with TypeScript

Extending instance/static functions on existing prototypes with TypeScript

Whilst TypeScript seems to be maturing quite well along it's road to version 1.0, I'd noticed quite early on, in it's infancy that it was not easy to polyfill/shim/monkey patch/extend the functionality of core objects (Object, String, Number, Boolean etc) using TypeScript...however this is perfectly legal in pure JavaScript, and in my opinion, necessary for creating cross browser compatible and compliant APIs.

Now that we are on version 0.9.5 I've noticed much improvement in this area, and we can now extend the prototype of core objects, and it would seem that we can also add static functions to core objects too...HOWEVER...this appears to be buggy!

Consider the following code example:

interface Object {
    /********************************/
    /*  Static functions            */
    /********************************/

    // ECMAScript 6 Object.is function
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
    is(v1: any, v2: any): boolean;

    /********************************/
    /*  Prototype functions         */
    /********************************/

    // Custom method for testing obejct equality (based on Java / C# principles)
    equals(obj: any): boolean;
}

// Object.is polyfill
(() => {
    if(!Object.is) {
        Object.is = function(v1: any, v2: any): boolean {
            if (v1 === 0 && v2 === 0) {
                return 1 / v1 === 1 / v2;
            }
            if (v1 !== v1) {
                return v2 !== v2;
            }
            return v1 === v2;
        }
    }
})();

// Object.prototype.equals implementation
Object.prototype.equals = function(obj: any): boolean {
    return Object.is(obj, this);
}

Wanna play with it?...Playground

You've probably already noticed what's wrong here...interfaces shouldn't contain definitions for static functions, only those which should be bound to the prototype. In this respect, if you check out lib.d.ts, you will see that static functions are declared differently:

declare var Object: {
    new (value?: any): Object;
    (): any;
    (value: any): any;
    prototype: Object;
    getPrototypeOf(o: any): any;
    getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor;
    getOwnPropertyNames(o: any): string[];
    create(o: any, properties?: PropertyDescriptorMap): any;
    defineProperty(o: any, p: string, attributes: PropertyDescriptor): any;
    defineProperties(o: any, properties: PropertyDescriptorMap): any;
    seal(o: any): any;
    freeze(o: any): any;
    preventExtensions(o: any): any;
    isSealed(o: any): bool;
    isFrozen(o: any): bool;
    isExtensible(o: any): bool;
    keys(o: any): string[];
}

So that means that we should really be adding is(v1: any, v2: any): boolean; to the declaration...but declarations in TypeScript are not open-ended as are interfaces, therefore this is not possible.

That being said, if I remove the declaration from the interface, TypeScript then complains that Object.is does not exist...so it would appear that I HAVE TO have it in there for the polyfill to work!

"Well if it works...kinda what's the problem?" - Yes it works, but only just and it's not elegant by any means.

Because is has been implemented statically, yet it is contracted as part of Object, TypeScript intellisense is still expecting Object.prototype.is AND does not recognize the existence of Object.is

I feel like I'm waffling now so I'll get to the point.

  1. Is this a known issue?
  2. have I missed something?
  3. What is in the pipeline to rectify this issue?

NOTE: I've just noticed, it appears that you can overwrite declarations now, so you could add is(v1: any, v2: any): boolean; to the declaration by overwriting it completely...but I still feel that this is not the most elegant solution.

Was it helpful?

Solution

This concern has been raised. The main reasons for not naming the type of the Object var were that we wanted to avoid making a name at all (what would you call it?), and that it would make it more difficult in the future to transition this type into a class shape without causing a breaking change.

The best solution on your end is to have a custom lib.d.ts for your project; as long as the compiler can find some types with the names of the primitive types (Number, String, etc) it doesn't actually matter what the content of lib.d.ts is. So you could either have e.g. lib-es6.d.ts that added the new members directly to those anonymous types, or have e.g. lib-extensible.d.ts that changed the anonymous types into named interfaces so you could extend them in a polyfill definition file elsewhere.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top