15

I recently asked a question about TypeScript's ability to extend existing prototypes in the JavaScript API (here: Extending Object.prototype with TypeScript).

This turned out to be a bug, which has since been resolved as of TypeScript 0.9.0 Alpha (which now includes generics...GREAT :-))

In TypeScript, interfaces are open ended, so if you look in lib.d.ts, you will find an interface which defines the contract for JavaScript's Object API. You should also see a variable declaration for Object, which defines Object's static functions.

For the sake of simplicity, here they are:

//Pulled from lib.d.ts

interface Object {
    toString(): string;
    toLocaleString(): string;
    valueOf(): Object;
    hasOwnProperty(v: string): bool;
    isPrototypeOf(v: Object): bool;
    propertyIsEnumerable(v: string): bool;
    [s: string]: any;
}

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, my previous question was about extending the interface for Object, like so:

interface Object {
    GetFoo(): Foo;
}

Object.prototype.GetFoo = function() {
    return new Foo();
}

As stated, this works as of TypeScript 0.9.0.

But now I also want to be able to add static functions to Object, perfectly legal in pure JavaScript.

In JavaScript I would do:

Object.FooAgain = function () {
    // Yes, it's foo again!
}

But I can't seem to be able to accomplish this in TypeScript.

First I tried to see if Object's declaration was open ended (as with interfaces):

declare var Object: {
    FooAgain(): void;
}

Object.FooAgain = function () {
    // TS doesn't like this, and Object.FooAgain isn't available in intellisense.
}

I also tried (I saw this in another article, and thought it was worth a try).

export declare var Object: {
}

but this seems to break everything even more...

Admittedly this issue may have also been fixed in TS 0.9.0 (I'm at work so I haven't actually tried it fully).

Can anyone shed some light on this?

Community
  • 1
  • 1
Matthew Layton
  • 39,871
  • 52
  • 185
  • 313

3 Answers3

14

Since TypeScript 1.4 static extensions can be added easily. The TypeScript team changed the lib.d.ts file to use interfaces for all static type definitions.

The static type definitions are all named like [Type]Constructor: So if you want to add a static function to type Object, then add your definition to ObjectConstructor.

Definition:

interface ObjectConstructor
{
    FooAgain(): void;
}

Implementation:

Object.FooAgain = function(): void
{
    // Oh noes, it's foo again!
}
Stefan Born
  • 720
  • 6
  • 14
  • well, in my case this didn't work, I tried to add a static function to DateConstructor, but I received this error: "date_extensions.ts:75 Uncaught ReferenceError: DateConstructor is not defined at date_extensions.ts:75:1" – Alan Donizete Feb 06 '23 at 20:40
3

The problems you are encountering are the following:

Declarations of variables are not open like interfaces, so you can't add properties to an existing declare var ....

Because it is declare var and not declare class you can't extend Object using inheritance.

So you can't get the full experience from TypeScript in this scenario. You can only get the compromise of:

Object['FooAgain'] = function () {
    alert('Again?');
}

Object['FooAgain']();

If you want the full TypeScript experience, I recommend you create a class to house the static methods - they don't operate on an instance anyhow, so there is little harm in doing this:

module Custom {
    export class Object {
        static FooAgain() {
            alert('Again?');
        }
    }
}

Custom.Object.FooAgain();
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Well, that sucks...I think maybe they should try factor this in before releasing TypeScript 1.0...But thanks for your answer. This clears up my concerns. – Matthew Layton May 29 '13 at 16:29
  • Update: I've raised this concern through the Bug Tracker @ typescript.codeplex.com...AFAIC it should be allowed because it's allowed in JavaScript, and there's no point trying to leverage JavaScript into the new language if it doesn't support the basics. – Matthew Layton May 29 '13 at 16:38
3

UPDATE

It is now possible since TS 1.4. See Stefan's answer above

LEGACY

I have this bug raised for a while now: https://typescript.codeplex.com/workitem/917

One solution that the typescript team could have (without extending the language spec) is have used interfaces for the static members and constructors : http://basarat.github.io/TypeScriptDeepDive/#/modellingstatics

Basically if lib.d.ts had:

//Pulled from lib.d.ts

interface Object {
    toString(): string;
    toLocaleString(): string;
    valueOf(): Object;
    hasOwnProperty(v: string): bool;
    isPrototypeOf(v: Object): bool;
    propertyIsEnumerable(v: string): bool;
    [s: string]: any;
}

interface ObjectStatic {
    new (value?: any): Object;
    (): any;
    (value: any): any;
    prototype: Object;
    ...
}

declare var Object: ObjectStatic; 

Then you could have easily added members to Object via:

interface ObjectStatic {
    FooAgain(): void;
}

Object.FooAgain = function () {
    // TS would be fine with this. 
}

I just created a feature request for this as well : https://typescript.codeplex.com/workitem/1085

gabriel_vincent
  • 1,230
  • 3
  • 16
  • 35
basarat
  • 261,912
  • 58
  • 460
  • 511
  • That's certainly one approach that would work, however my concern here is that ObjectStatic isn't something that could be called in JavaScript, and makes the implementation look a little haskish, therefore, it seems more appropriate that they make the declaration open ended, as with the interfaces. – Matthew Layton May 30 '13 at 08:17
  • In fact, reading your workitem (917), I prefer this implementation, as this looks a lot cleaner than "declare var". Also Object cannot be extended in TypeScript, and it can in JavaScript (i know this happens naturally, but the principle should still apply in TypeScript) – Matthew Layton May 30 '13 at 08:19
  • Afraid not....It's a part of our guide as well http://definitelytyped.org/guides/best-practices.html – basarat Jul 02 '14 at 09:36
  • This is possible since TypeScript 1.4: All static definitions are named like `[Type]Constructor` – Stefan Born Jan 19 '15 at 08:57
  • Are you sure about this? TypeScript 2.5 here and HTMLElement is 'declare var HTMLElement' and not HTMLElementConstructor. – olivierr91 Nov 12 '17 at 16:40