Вопрос

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?

Это было полезно?

Решение 3

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 -- */
    }
}

Другие советы

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.

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());
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top