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?
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());
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 -- */
}
}