1

I'm trying to create a TypeScript 1.0.2 (VS 2013 Update 2 RTM) definition file for the ASP.NET Ajax library, and I'm getting hung up on how to define the additional methods that MS Ajax adds to base JS types such as Array. I created a AspNetAjax.d.ts and a AspNetAjax-tests.ts file. When I attempt to use the "add" method in the test file, I get the compiler error that is listed below.

AspNetAjax.d.ts

interface Array<T> {
    add(array: T[], item: T): void;
}

AspNetAjax-tests.ts

///<reference path="AspNetAjax.d.ts" /> 

var a: string[] = ['a', 'b', 'c', 'd'];
Array.add(a, 'e');
console.log(a.toString());

Error 1 The property 'add' does not exist on value of type '{ isArray(arg: any): boolean; prototype: any[]; (arrayLength?: number): any[]; (arrayLength: number): T[]; (...items: T[]): T[]; new(arrayLength?: number): any[]; new(arrayLength: number): T[]; new(...items: T[]): T[]; }'. c:\path\AspNetAjax-tests.ts 4 7 TypeScriptHTMLApp1

Other definitions from the same d.ts file are working in the tests file so I know that the reference is physically working. TypeScript also doesn't complain about the way I've declared the d.ts file (no red squiggles there).

I am aware of these other questions and I thought I was doing what they suggested, but it seems they're from late 2012/early 2013 so perhaps the way to do this has changed since then?

Extending Array in TypeScript

Adding a property to Array in Typescript

How can I add a static method to an existing type?

I need to code the d.ts so that the code in the .ts file will work. Any ideas?

Community
  • 1
  • 1
NYCdotNet
  • 4,500
  • 1
  • 25
  • 27
  • Very simple solution - I've posted here: http://stackoverflow.com/questions/14867649/adding-a-property-to-array-in-typescript/33573875#33573875 – Vukasin Nov 06 '15 at 19:06

3 Answers3

2

The code you've written adds an add member to Array instances, not the built-in Array object.

The Array built-in object is defined in lib.d.ts near line 1134:

declare var Array: {
    new (arrayLength?: number): any[];
    new <T>(arrayLength: number): T[];
    new <T>(...items: T[]): T[];
    (arrayLength?: number): any[];
    <T>(arrayLength: number): T[];
    <T>(...items: T[]): T[];
    isArray(arg: any): boolean;
    prototype: Array<any>;
}

If you want to add a member to Array, you can modify the declaration as it appears in lib.d.ts.

If you're thinking that messing with lib.d.ts seems like a bad idea, it's because you shouldn't modify the built-in objects. There's no way to be sure that you and someone else haven't both decided that you have a great idea for an Array.add method that have completely different behavior.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • Hi Ryan - many thanks. You're correct: if I change the .ts file to `a.add(a, 'e');`, it indeed compiles without error. I completely agree with you regarding how modifying the built-in objects is something you shouldn't do. However, the [ASP.NET Ajax library does it](http://msdn.microsoft.com/en-us/library/bb397506(v=vs.100).aspx). With the understanding that this is "bad", is editing the lib.d.ts the only way to allow TypeScript to compile code from old ASP.NET sites that use the Ajax library and take advantage of its "helpers"? – NYCdotNet May 13 '14 at 18:37
2

You can take advantage of the declaration merging logic in TypeScript to extend Array as shown :

declare module Array{
    export var add:Function;
}

var a: string[] = ['a', 'b', 'c', 'd'];
Array.add(a, 'e'); // Okay now
console.log(a.toString());
basarat
  • 261,912
  • 58
  • 460
  • 511
  • Thanks Basarat, but I'm getting a "Duplicate identifier 'Array'." error with that declare block in TypeScript 1.0. I guess this is the same error from the third linked question. Perhaps editing the lib.d.ts file is the only way for now. – NYCdotNet May 14 '14 at 14:16
  • Hmmm it works here, and in the typescript playground – basarat May 14 '14 at 14:28
  • 1
    I also see it working on the playground (though it removes the other properties on Array so they'd have to be re-implemented). I am getting the error in VS 2013 Update 2 (so TS 1.0.1) with compile on save and also with tsc.exe 1.0.1.0 from the command line. – NYCdotNet May 14 '14 at 16:05
1

Between @basarat and @Ryan-Cavanaugh's answers I was able to come up with a minimally-horrible-to-the-user solution that works with TypeScript 1.0, as long as one is willing to accept that modifying the built-in JS objects is generally bad idea so it's OK if supporting that in TypeScript is slightly awkward.

Assuming that I am defining the ASP.NET AJAX extensions on the built-in JS Array object:

  • I will declare a module in the d.ts file called AspNetAjaxExtensions
  • I will create a non-exported interface inside that module declaration called JavaScriptArray<T>
  • I will create an exported interface inside that module declaration called Array<T> that extends JavaScriptArray<T>
  • I will copy all of the definitions for Array and Array<T> from the lib.d.ts that ships with TypeScript into the new JavaScriptArray<T> interface.
  • I can then proceed to model out only the extended functionality inside the new Array<T> interface.

This is somewhat anti-DRY for the d.ts author, but in reality these things very infrequently change and the stuff in JavaScriptArray<T> that was copied from lib.d.ts is self-contained. Technically, a build step could even be created to dynamically fill-in this interface.

For the user, they have to change their code that calls the extended Array object from this:

//will be an error in TypeScript 1.0
Array.add(x, 'thing');

to this:

//This works in TypeScript 1.0 after importing the above-described d.ts file.
(<AspNetAjaxExtensions.Array<string>>Array).add(x, 'thing');

Feasibly one could even Find+Replace Array.add( with (<AspNetAjaxExtensions.Array<any>>Array).add(.

They only need to do this whenever any of the extended methods on the built-in Array object are called (which will be called out by TypeScript syntax errors). Calls to normal Array methods will still use the "normal" definition in lib.d.ts.

Example new AspNetAjax.d.ts file:

declare module AspNetAjaxExtensions {

    /** This interface definition was copied from lib.d.ts */
    interface JavaScriptArray<T> {
        new (arrayLength?: number): any[];
        new <T>(arrayLength: number): T[];

        /* -- Snip out many copied and pasted lines of code from lib.d.ts -- */

    }

    /** JavaScript Array object as extended by ASP.NET Ajax */
    export interface Array<T> extends JavaScriptArray<T>  {

        /** Adds an element to the end of an Array object.  This function is static and is invoked without creating an instance of the object.
          http://msdn.microsoft.com/en-us/library/bb310854(v=vs.100).aspx
          @param array The array to add the item to.
          @param item The object to add to the array.
        */
        add(array: T[], item: T): void;

        /* -- etc... defining remaining extended methods -- */
    }
}
NYCdotNet
  • 4,500
  • 1
  • 25
  • 27