TypeScript中的构造函数重载

TypeScript中的构造函数重载,typescript,constructor,overloading,Typescript,Constructor,Overloading,有人在TypeScript中做过构造函数重载吗。在语言规范(V0.8)的第64页,有描述构造函数重载的语句,但并没有给出任何示例代码 我现在正在尝试一个真正基本的类声明;看起来是这样的, interface IBox { x : number; y : number; height : number; width : number; } class Box { public x: number; public y: number;

有人在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;
    }   
}
编辑: 从2016年12月5日起,请参阅以获得更为详细、更为灵活的解决方案。

更新2(2020年9月28日):这种语言在不断发展,因此,如果您可以使用
部分(在v2.1中介绍),那么现在这是我实现这一目标的首选方法

类框{
x:数字;
y:数字;
高度:数字;
宽度:数字;
公共构造函数(b:Partial={}){
对象。分配(此,b);
}
}
//示例使用
常数a=新框();
常数b=新框({x:10,高度:99});
常数c=新框({foo:10});//将无法编译
更新(2017年6月8日):圭亚拉和斯诺夫莱克在下面对我的回答的评论中提出了有效的观点。我建议读者看看我的答案,谁的答案比我的好*

原始答案(2014年1月27日)

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

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;
    }
  }
}
上课时间{
私人日期:日期;
私人亲戚小时数;
建造商(年:编号、月:编号、日:编号、相对时:编号);
建造商(日期:日期,相对时间:编号);
构造函数(dateOrYear:any,monthOrRelativeHour:number,day?:number,relativeHour?:number){
如果(日期的类型EAR==“编号”){
this.date=新日期(dateOrYear,monthOrRelativeHour,day);
this.relativeHour=relativeHour;
}否则{
var date=dateOrYear;
this.date=新日期(date.getFullYear(),date.getMonth(),date.getDate());
this.relativeHour=monthorrativehour;
}
}
}

来源:

我知道这是一个老问题,但1.4中的新问题是联合类型;对所有函数重载(包括构造函数)使用这些函数。例如:

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

关于构造函数重载一个好的选择是将附加重载作为静态工厂方法来实现。我认为它比在构造函数中检查所有可能的参数组合更容易阅读

在下面的示例中,我们可以使用保险提供商提供的数据创建一个patient对象,该数据以不同的方式存储值。为了支持患者实例化的另一个数据结构,可以简单地添加另一个静态方法,以便在规范化所提供的数据后尽可能调用默认构造函数

分类患者{
静态保险({
首先,中间,最后,
生日、性别
}:保险客户):病人{
在此返回新的(
`${last},${first}${middle}`.trim(),
utils.年龄(生日),
性别
);
}
建造师(
公共名称:string,
公众年龄:人数,
公共性别?:字符串
) {}
}
接口保险客户{
第一:字符串,
中间?:字符串,
最后:弦,,
生日:弦,
性别:“M”|“F”
}
const utils={/*包含在下面的游乐场链接中*/};
{//创建患者实例的两种方法
常数
jane=新患者('Doe,jane',21),
alsoJane=患者。来自保险({
第一个:“简”,最后一个:“多伊”,
生日:2000年1月1日,性别:F
})
控制台清除()
console.log(jane)
控制台日志(alsoJane)
}
您可以在以下位置检查输出:



比如说,TypeScript中的方法重载不是真的,因为它需要太多编译器生成的代码,而TS的设计是为了不惜一切代价避免这种情况。方法重载的主要用例可能是为在API中具有神奇参数的库编写声明。由于所有处理不同可能参数集的繁重工作都是由您完成的,因此我不认为在每个场景中使用重载而不是特殊方法有什么好处。

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

export class Track {
   public title: string;
   public artist: string;
   public lyrics: string;

   constructor(track?: Track) {
     Object.assign(this, track);
   }
}

请记住,这将分配传入
轨道的所有属性,如果它们未在
轨道上定义,则eve听起来您希望对象参数是可选的,并且对象中的每个属性也是可选的。在本例中,如所提供的,不需要重载语法。我想指出一些答案中的错误做法。诚然,它不是基本上编写
box={x:0,y:87,width:4,height: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(obj: IBox) {
        const { x, y, height, width } = { x: 0, y: 0, height: 0, width: 0, ...obj }
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
}
 /** @class */
 class Box {
     public x?: number;
     public y?: number;
     public height?: number;
     public width?: number;     

     constructor(params: Box = {} as Box) {

         // Define the properties of the incoming `params` object here. 
         // Setting a default value with the `= 0` syntax is optional for each parameter
         let {
             x = 0,
             y = 0,
             height = 1,
             width = 1
         } = params;
         
         //  If needed, make the parameters publicly accessible
         //  on the class ex.: 'this.var = var'.
         /**  Use jsdoc comments here for inline ide auto-documentation */
         this.x = x;
         this.y = y;
         this.height = height;
         this.width = width;
     }
 }
需要添加方法吗?详细但更可扩展的替代方案: 上面的
类可以兼作接口,因为它们是相同的。如果选择修改上述类,则需要为传入参数对象定义和引用一个新接口,因为
类n
    interface BoxParams {
        x?: number;
         // Add Parameters ...
    }

    class Box {
         public x: number;
         // Copy Parameters ...
         constructor(params: BoxParams = {} as BoxParams) {
         let { x = 0 } = params;
         this.x = x;
    }
    doSomething = () => {
        return this.x + this.x;
        }
    }

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 Box = (function () {
    function Box(params) {
        if (params === void 0) { params = {}; }
        var _a = params.x, x = _a === void 0 ? 0 : _a, _b = params.y, y = _b === void 0 ? 0 : _b, _c = params.height, height = _c === void 0 ? 1 : _c, _d = params.width, width = _d === void 0 ? 1 : _d;
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
    return Box;
}());
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);
class BoxTest {
    public x?: number = 1;

    constructor(params: BoxTest = {} as BoxTest) {
        Object.assign(this, params);
    }
}
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 params.
console.assert(typeof box.z === 'undefined')
contructor()

constructor(a:any, b:any, c:any)
constructor(a?:any, b?:any, c?:any)
class Box {
  x: number;
  y: number;
  height: number;
  width: number;
  constructor(obj?: Partial<Box>) {    
     assign(this, obj);
  }
}
class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor();
    constructor(obj: IBox);
    constructor(obj?: IBox) {    
        this.x = !obj ? 0 : obj.x;
        this.y = !obj ? 0 : obj.y;
        this.height = !obj ? 0 : obj.height;
        this.width = !obj ? 0 : obj.width;
    }
}
private x?: number;
private y?: number;

constructor({x = 10, y}: {x?: number, y?: number}) {
 this.x = x;
 this.y = y;
}
interface IBox = {
    x: number;
    y: number;
    width: number;
    height: number;
}

class Box {
    public x: number;
    public y: number;
    public width: number;
    public height: number;

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        } else {
            this.x = 0;
            this.y = 0;
            this.width = 0;
            this.height = 0
        }
    }

    get frame(): string {
        console.log(this.x, this.y, this.width, this.height);
    }
}

new Box().frame; // 0 0 0 0
new Box({ x:10, y:10, width: 70, height: 120 }).frame; // 10 10 70 120



// You could also write the Box class like so;
class Box {
    public x: number = 0;
    public y: number = 0;
    public width: number = 0;
    public height: number = 0;

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        }
    }

    get frame(): string { ... }
}
class LocalError {
  message?: string;
  status?: string;
  details?: Map<string, string>;

  constructor(message: string);
  constructor(message?: string, status?: string);
  constructor(message?: string, status?: string, details?: Map<string, string>) {
    this.message = message;
    this.status = status;
    this.details = details;
  }
}
/** @class */

class Box {
  public x?: number;
  public y?: number;
  public height?: number;
  public width?: number;

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: Box = {} as Box) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  If needed, make the parameters publicly accessible
    //  on the class ex.: 'this.var = var'.
    /**  Use jsdoc comments here for inline ide auto-documentation */
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
  }

  method(): void {
    const total = this.x + 1; // ERROR. Object is possibly 'undefined'.ts(2532)
  }
}

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 });
method(): void {
    const total = <number> this.x + 1;
}
Argument of type '{ x: number; y: number; width: number; height: number; }' is not 
assignable to parameter of type 'Box'.
Property 'method' is missing in type '{ x: number; y: number; width: number; height: 
number; }' but required in type 'Box'.ts(2345)
type BoxParams = {
  x?: number;
  y?: number;
  height?: number;
  width?: number;
}

/** @class */
class Box {
  public x: number;
  public y: number;
  public height: number;
  public width: number;

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: BoxParams = {} as BoxParams) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  If needed, make the parameters publicly accessible
    //  on the class ex.: 'this.var = var'.
    /**  Use jsdoc comments here for inline ide auto-documentation */
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
  }

  method(): void {
    const total = this.x + 1;
  }
}

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 });