如何在TypeScript中扩展本地JavaScript数组
有什么办法从JS本地函数inheritance一个类。
对于(eg)我有js在我的js中像这样的function。
function Xarray() { Array.apply(this, arguments); //some stuff for insert, add and remove notification } Xarray.prototype = new Array();
我试图将其转换为Typescript,但我失败了!
export class Xarray implements Array { }
比较器要求我定义所有的数组接口属性,我知道是否需要这个Xarray.prototype = new Array();
我必须在TS中扩展数组,如何在TS中扩展JS本地对象?
我不认为有一种方法来inheritance像Array这样的现有接口,
export class Xarray implements Array { }
你应该创build一个函数并且用它的原型inheritance它。 Typescript也将接受它类似于JavaScript。
function Xarray(...args: any[]): void; // required in TS 0.9.5 function Xarray() { Array.apply(this, arguments); // some stuff for insert, add and remove notification } Xarray.prototype = new Array();
更新:这个讨论很好,并提供了最好的解决scheme,在这个jqfaq.com 。
//a dummy class it to inherite array. class XArray { constructor() { Array.apply(this, arguments); return new Array(); } // we need this, or TS will show an error, //XArray["prototype"] = new Array(); will replace with native js arrray function pop(): any { return "" }; push(val): number { return 0; }; length: number; } //Adding Arrray to XArray prototype chain. XArray["prototype"] = new Array(); //our Class class YArray extends XArray { ///Some stuff } var arr = new YArray(); //we can use the array prop here. arr.push("one"); arr.push("two"); document.writeln("First Elemet in array : " + arr[0]); document.writeln("</br>Array Lenght : " + arr.length);
希望,这可能会帮助你!
从TypeScript 1.6开始,您可以扩展数组types,请参阅TypeScript中的新增function
这是一个例子:
class MyNewArray<T> extends Array<T> { getFirst() { return this[0]; } } var myArray = new MyNewArray<string>(); myArray.push("First Element"); console.log(myArray.getFirst()); // "First Element"
是的,可以在TS中扩展本地JS对象,但是像Array一样扩展内置types(包含在lib.d.ts中)是一个问题。 阅读这个职位的解决方法: http : //typescript.codeplex.com/workitem/4
因此定义一个types接口,在后面的阶段扩展一个本地types的对象可以用下面的方法完成:
/// <reference path="lib.d.ts"/> interface Array { sort: (input: Array) => Array; }
使用一个具体的例子,你可以对数组中的一些元素进行sorting,这些元素在一个接口中定义一个sorting函数,然后在一个对象上实现。
class Math implements Array { sort : (x: Array) => Array { // sorting the array } } var x = new Math(); x.sort([2,3,32,3]);
在研究这个时,我遇到了Ben Nadel关于扩展JavaScript数组,同时保持原生括号注释function的优秀post。 在对如何成功将其转换为TypeScript的初步疑惑之后,我创build了一个可以被子类化的完全有效的Collection类。
它可以完成数组所能做的所有事情,包括括号索引,循环结构(for,while,forEach),地图等。
主要的实施点是
- 在构造函数中创build一个数组,将这些方法添加到数组中并从构造函数中返回
- 复制Array方法的虚拟声明以传递
implements Array
位
使用示例:
var foo = new Foo({id : 1}) var c = new Collection(); c.add(foo) c.length === 1; // => true foo === c[0]; // => true foo === c.find(1); // => true
我把它作为一个要点提供 ,完成了testing和一个子类的示例实现,但是我在这里提供了完整的源代码:
/* * Utility "class" extending Array with lookup functions * * Typescript conversion of Ben Nadel's Collection class. * https://gist.github.com/fatso83/3773d4cb5f39128b3732 * * @author Carl-Erik Kopseng * @author Ben Nadel (javascript original) */ export interface Identifiable { getId : () => any; } export class Collection<T extends Identifiable> implements Array<T> { constructor(...initialItems:any[]) { var collection = Object.create(Array.prototype); Collection.init(collection, initialItems, Collection.prototype); return collection; } static init(collection, initialItems:any[], prototype) { Object.getOwnPropertyNames(prototype) .forEach((prop) => { if (prop === 'constructor') return; Object.defineProperty(collection, prop, { value: prototype[prop] }) }); // If we don't redefine the property, the length property is suddenly enumerable! // Failing to do this, this would fail: Object.keys([]) === Object.keys(new Collection() ) Object.defineProperty(collection, 'length', { value: collection.length, writable: true, enumerable: false }); var itemsToPush = initialItems; if (Array.isArray(initialItems[0]) && initialItems.length === 1) { itemsToPush = initialItems[0]; } Array.prototype.push.apply(collection, itemsToPush); return collection; } // Find an element by checking each element's getId() method public find(id:any):T; // Find an element using a lookup function that // returns true when given the right element public find(lookupFn:(e:T) => boolean):T ; find(x:any) { var res, comparitor; if (typeof x === 'function') { comparitor = x; } else { comparitor = (e) => { return e.getId() === x; } } res = [].filter.call(this, comparitor); if (res.length) return res[0]; else return null; } // Add an element add(value:T); // Adds all ements in the array (flattens it) add(arr:T[]); add(arr:Collection<T>); add(value) { // Check to see if the item is an array or a subtype thereof if (value instanceof Array) { // Add each sub-item using default push() method. Array.prototype.push.apply(this, value); } else { // Use the default push() method. Array.prototype.push.call(this, value); } // Return this object reference for method chaining. return this; } remove(elem:T):boolean; remove(lookupFn:(e:T) => boolean):boolean ; remove(x:any):boolean { return !!this._remove(x); } /** * @return the removed element if found, else null */ _remove(x:any):T { var arr = this; var index = -1; if (typeof x === 'function') { for (var i = 0, len = arr.length; i < len; i++) { if (x(this[i])) { index = i; break; } } } else { index = arr.indexOf(x); } if (index === -1) { return null; } else { var res = arr.splice(index, 1); return res.length ? res[0] : null; } } // dummy declarations // "massaged" the Array interface definitions in lib.d.ts to fit here toString:()=> string; toLocaleString:()=> string; concat:<U extends T[]>(...items:U[])=> T[]; join:(separator?:string)=> string; pop:()=> T; push:(...items:T[])=> number; reverse:()=> T[]; shift:()=> T; slice:(start?:number, end?:number)=> T[]; sort:(compareFn?:(a:T, b:T) => number)=> T[]; splice:(start?:number, deleteCount?:number, ...items:T[])=> T[]; unshift:(...items:T[])=> number; indexOf:(searchElement:T, fromIndex?:number)=> number; lastIndexOf:(searchElement:T, fromIndex?:number)=> number; every:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean; some:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean; forEach:(callbackfn:(value:T, index:number, array:T[]) => void, thisArg?:any)=> void; map:<U>(callbackfn:(value:T, index:number, array:T[]) => U, thisArg?:any)=> U[]; filter:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> T[]; reduce:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U; reduceRight:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U; length:number; [n: number]: T; }
当然,可Identifiable
, find
和remove
方法是不需要的,但是我提供它们的东西,因为一个完整的例子比没有任何方法的纯粹集合更有用。
在你的情况下,一个好的select是使用这种模式:
function XArray(array) { array = array || []; //add a new method array.second = function second() { return array[1]; }; //overwrite an existing method with a super type pattern var _push = array.push; array.push = function push() { _push.apply(array, arguments); console.log("pushed: ", arguments); }; //The important line. return array }
那你可以这样做:
var list = XArray([3, 4]); list.second() ; => 4 list[1] = 5; list.second() ; => 5
但请注意:
list.constructor ; => Array and not XArray
如果你已经有了一个工作的Xarray
实现,我没有看到重新Xarray
它的脚本,最终将编译回JavaScript。
但是我确实能够在TypeScript中使用Xarray
。
为了做到这一点,你只需要一个Xarray
的接口。 你甚至不需要具体实现你的接口,因为你现有的js实现将作为一个实现。
interface Xarray{ apply(...arguments : any[]) : void; //some stuff for insert, add and ... } declare var Xarray: { new (...items: any[]): Xarray; (...items: any[]): Xarray; prototype: Array; // This should expose all the Array stuff from ECMAScript }
做完这些之后,应该可以通过声明的variables来使用自定义的types,而不用在TypeScript中实现。
var xArr = new Xarray(); xArr.apply("blah", "hehe", "LOL");
您可以在这里查找参考,了解他们如何键入ECMAScript Array API
: http : //typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts
返回一个对象的构造函数隐式地将这个值replace为super()
调用者。 生成的构造函数代码必须捕获任何super()
返回并用this
代替它。
内build的类使用ES6 new.target
来做修正,但是ES5代码没有办法确保new.target有一个调用构造函数的值。
这就是为什么你的额外的方法消失 – 你的对象有错误的原型。
所有你需要做的就是在调用super()
之后修复原型链。
export class RoleSet extends Array { constructor() { super(); Object.setPrototypeOf(this, RoleSet.prototype); } private containsRoleset(roleset:RoleSet){ if (this.length < roleset.length) return false; for (var i = 0; i < roleset.length; i++) { if (this.indexOf(roleset[i]) === -1) return false; } return true; } public contains(item: string | RoleSet): boolean { if (item) { return typeof item === "string" ? this.indexOf(item) !== -1 : this.containsRoleset(item); } else { return true; } } }
要知道,这个诅咒应该折磨你的孩子和你的孩子的孩子,直到代码结束。 你必须在inheritance链的每一代中进行修复。
是的,你可以增加内buildtypes,并且不需要像其他答案中所描述的那样需要XArray的所有工具,并且更接近于你如何用javascript来完成。
Typescript允许有多种方式来实现这一点,但是对于像Array和Number这样的Builtintypes,您需要使用“合并”并声明全局名称空间来扩充types,请参阅文档
所以对于数组我们可以添加一个可选的元数据对象并获得第一个成员
declare global { interface Array<T> { meta?: any|null , getFirst(): T } } if(!Array.prototype.meta ) { Array.prototype.meta = null } if(!Array.prototype.getFirst ) { Array.prototype.getFirst = function() { return this[0]; } }
我们可以这样使用:
let myarray: number[] = [ 1,2,3 ] myarray.meta = { desc: "first three ints" } let first: number = myarray.getFirst()
同样的数字说,我想添加一个模数函数,不限于像余数%
declare global { interface Number { mod(n:number): number } } if(!Number.prototype.mod ) { Number.prototype.mod = function (n: number) { return ((this % n) + n) % n; } }
我们可以像这样使用它:
let foo = 9; console.log("-9.mod(5) is "+ foo.mod(5))
对于我们可能想要添加一个API的函数,即使它像一个函数和一个对象一样行为,我们可以使用混合types(参见文档)
// augment a (number) => string function with an API interface Counter { (start: number): string; interval: number; reset(): void; } //helper function to get my augmented counter function with preset values function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; }
像这样使用它:
let c = getCounter(); c(10); c.reset(); c.interval = 5.0;
不知道这是如何皱眉,但例如我需要创build一个BulletTypes的数组,以便我可以循环。 我做的是以下几点:
interface BulletTypesArray extends Array<BulletType> { DefaultBullet?: Bullet; } var BulletTypes: BulletTypesArray = [ GreenBullet, RedBullet ]; BulletTypes.DefaultBullet = GreenBullet;
显然你也可以做一个通用的接口,像interface SuperArray<T> extends Array<T>
。