在TypeScript中构造器重载

有没有人在TypeScript中完成构造函数的重载。 在语言规范(v0.8)的第64页中,有描述构造函数重载的语句,但是没有给出任何示例代码。

我正在尝试一个非常基本的类声明, 它看起来像这样,

interface IBox { x : number; y : number; height : number; width : number; } class Box { public x: number; public y: number; public height: number; public width: number; constructor(obj: IBox) { this.x = obj.x; this.y = obj.y; this.height = obj.height; this.width = obj.width; } constructor() { this.x = 0; this.y = 0; this.width = 0; this.height = 0; } } 

当用tsc BoxSample.ts运行时,会抛出一个重复的构造函数定义 – 这是显而易见的。 任何帮助表示赞赏。 提前致谢。

TypeScript允许你声明重载,但你只能有一个实现,并且实现必须有一个与所有重载兼容的签名。 在你的例子中,这可以很容易地用一个可选参数来完成,

 interface IBox { x : number; y : number; height : number; width : number; } class Box { public x: number; public y: number; public height: number; public width: number; constructor(obj?: IBox) { this.x = obj && obj.x || 0 this.y = obj && obj.y || 0 this.height = obj && obj.height || 0 this.width = obj && obj.width || 0; } } 

或者用更通用的构造函数重载两个,

 interface IBox { x : number; y : number; height : number; width : number; } class Box { public x: number; public y: number; public height: number; public width: number; constructor(); constructor(obj: IBox); constructor(obj?: any) { this.x = obj && obj.x || 0 this.y = obj && obj.y || 0 this.height = obj && obj.height || 0 this.width = obj && obj.width || 0; } } 

请注意,您还可以通过TypeScript中的默认参数解决实现级别的重载问题,例如:

 interface IBox { x : number; y : number; height : number; width : number; } class Box { public x: number; public y: number; public height: number; public width: number; constructor(obj : IBox = {x:0,y:0, height:0, width:0}) { this.x = obj.x; this.y = obj.y; this.height = obj.height; this.width = obj.width; } } 

编辑:截至16年12月5日,请参阅Benson的答案更详细的解决scheme,使更多的灵活性。

在TypeScript中重载方法并不是真的 ,因为它需要太多的编译器生成的代码,核心团队不惜一切代价尽量避免。 目前,语言中存在方法重载的主要原因是提供了一种方法来为其API中的带有魔术参数的库编写声明。 既然你需要自己完成所有的繁重工作来处理不同的参数集合,那么在使用重载代替分离的方法方面就没有多大优势了。

关于构造函数重载,另一种方法是将其他重载实现为静态工厂方法 。 我认为它比testing你的调用参数更可读,更容易混淆。 这是一个简单的例子:

 class Person { static fromData(data: PersonData) { let { first, last, birthday, gender = 'M' } = data return new this( `${last}, ${first}`, calculateAge(birthday), gender ) } constructor( public fullName: string, public age: number, public gender: 'M' | 'F' ) {} } interface PersonData { first: string last: string birthday: string gender?: 'M' | 'F' } let personA = new Person('Doe, John', 31, 'M') let personB = Person.fromData({ first: 'John', last: 'Doe', birthday: '10-09-1986' }) 

我知道这是一个古老的问题,但是新的1.4是联盟types; 使用这些函数重载(包括构造函数)。 例:

 class foo { private _name: any; constructor(name: string | number) { this._name = name; } } var f1 = new foo("bar"); var f2 = new foo(1); 

注意:这是简化和更新4/13/2017以反映Typescript 2.1,查看Typescript 1.8的答案的历史。

这听起来像你想要的对象参数是可选的,并且对象中的每个属性都是可选的。 在所提供的示例中,不需要重载语法。 我想在这里的一些答案中指出一些不好的做法。 当然,这并不是expression本质上最小的expression式, box = { x: 0, y: 87, width: 4, height: 0 } ,但是它提供了所有的代码, 这个例子允许你用任何,一些或者没有任何参数调用一个函数,并且仍然得到默认值。

  /** @class */ class Box { public x?: number; public y?: number; public height?: number; public width?: number; constructor(obj: Box = {} as Box) { let { x = 0, y = 0, height = 0, width = 0 } = obj; /** Hint: put jsdoc comments here for inline ide auto-documentation */ this.x = x; this.y = y; this.height = height; this.width = width; } } 

这是一个非常安全的方法来编写可能不具有定义的对象的所有属性的参数。 你现在可以安全地写下这些:

 const box1 = new Box(); const box2 = new Box({}); const box3 = new Box({x:0}); const box4 = new Box({x:0, height:10}); const box5 = new Box({x:0, y:87,width:4,height:0}); // Correctly reports error in Typescript, and in js, box6.z is undefined const box6 = new Box({z:0}); 

编译时,您会发现可选参数确实是可选的,这样可以避免var = isOptional || default;广泛使用(但容易出错)的回退语法的var = isOptional || default; var = isOptional || default; 通过检查void 0 ,这是undefined缩写:

编译输出

 var Box = (function () { function Box(obj) { if (obj === void 0) { obj = {}; } var _a = obj.x, x = _a === void 0 ? 1 : _a, _b = obj.y, y = _b === void 0 ? 1 : _b, _c = obj.height, height = _c === void 0 ? 1 : _c, _d = obj.width, width = _d === void 0 ? 1 : _d; this.x = x; this.y = y; this.height = height; this.width = width; } return Box; }()); 

附录:设置默认值:错误的方法

|| (或)运营商

考虑||的危险 /或操作员设置默认回退值时,如其他答案中所示。 下面的代码说明了设置默认值的错误方法。 在对像0,'',null,undefined,false,NaN等错误值进行评估时,您可能会得到意想不到的结果:

 var myDesiredValue = 0; var result = myDesiredValue || 2; // This test will correctly report a problem with this setup. console.assert(myDesiredValue === result && result === 0, 'Result should equal myDesiredValue. ' + myDesiredValue + ' does not equal ' + result); 

Object.assign(这一点,OBJ)

在我的testing中,使用es6 / typescript解构对象可比Object.assign快90% 。 使用解构参数只允许您分配给对象的方法和属性。 例如,考虑这个方法:

 class BoxTest { public x?: number = 1; constructor(obj: BoxTest = {} as BoxTest) { Object.assign(this, obj); } } 

如果另一个用户不使用打字稿,并尝试放置不属于的参数,比如他们可能会尝试放置一个z属性

 var box = new BoxTest({x: 0, y: 87, width: 4, height: 0, z: 7}); // This test will correctly report an error with this setup. `z` was defined even though `z` is not an allowed property of obj. console.assert(typeof box.z === 'undefined') 

更新(8/6/19): guyarad和snolflake在他们的评论中对我的回答作出了有效的评价。 我会build议读者看看Benson , Joe和snolflake的答案,他们比我的答案要好。

原答复(27/1/14)

另一个如何实现构造函数重载的例子:

 class DateHour { private date: Date; private relativeHour: number; constructor(year: number, month: number, day: number, relativeHour: number); constructor(date: Date, relativeHour: number); constructor(dateOrYear: any, monthOrRelativeHour: number, day?: number, relativeHour?: number) { if (typeof dateOrYear === "number") { this.date = new Date(dateOrYear, monthOrRelativeHour, day); this.relativeHour = relativeHour; } else { var date = <Date> dateOrYear; this.date = new Date(date.getFullYear(), date.getMonth(), date.getDate()); this.relativeHour = monthOrRelativeHour; } } } 

资料来源: http : //mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript

在可选的types化参数足够好的情况下,请考虑下面的代码,在不重复属性或定义接口的情况下完成相同的操作。

 export class Track { public title: string; public artist: string; public lyrics: string; constructor(track?: Track) { Object.assign(this, track); } } 

请记住,这将分配在track传递的所有属性,如果它们没有在Track定义的话。

另一个版本像@ ShinNoNoir的代码,使用默认值和扩展语法:

 class Box { public x: number; public y: number; public height: number; public width: number; constructor({x, y, height, width}: IBox = { x: 0, y: 0, height: 0, width: 0 }) { this.x = x; this.y = y; this.height = height; this.width = width; } }