有人在TypeScript中做过构造函数重载吗?在语言规范(v 0.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,它抛出一个重复的构造函数定义——这是显而易见的。任何帮助都是感激的。


当前回答

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

其他回答

一般来说,对于N个重载,最好使用:

constructor(obj?: {fromType1: IType1} | {fromType2: IType2}) {    
    if(obj){
      if(obj.fromType1){
        //must be of form IType1
      } else if(obj.fromType2){
        //must have used a IType2
      } else {
        throw "Invalid argument 1"
      }
    } else {
      //obj not given
    }
}   

至少现在我们可以确定走哪条路,并采取相应的行动

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?.x ?? 0
        this.y = obj?.y ?? 0
        this.height = obj?.height ?? 0
        this.width = 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?: IBox) {    
        this.x = obj?.x ?? 0
        this.y = obj?.y ?? 0
        this.height = obj?.height ?? 0
        this.width = 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日,请参阅本森的回答,以获得更灵活的更详细的解决方案。

Box类试图定义多个构造函数实现。

只有最后一个构造函数重载签名被用作类构造函数实现。

在下面的示例中,请注意构造函数实现的定义与前面的任何重载签名都不冲突。

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 Patient {
    static fromInsurance({
        first, middle = '', last,
        birthday, gender
    }: InsuranceCustomer): Patient {
        return new this(
            `${last}, ${first} ${middle}`.trim(),
            utils.age(birthday),
            gender
        );
    }

    constructor(
        public name: string,
        public age: number,
        public gender?: string
    ) {}
}

interface InsuranceCustomer {
    first: string,
    middle?: string,
    last: string,
    birthday: string,
    gender: 'M' | 'F'
}


const utils = { /* included in the playground link below */};

{// Two ways of creating a Patient instance
    const
        jane = new Patient('Doe, Jane', 21),
        alsoJane = Patient.fromInsurance({ 
            first: 'Jane', last: 'Doe',
            birthday: 'Jan 1, 2000', gender: 'F'
        })

    console.clear()
    console.log(jane)
    console.log(alsoJane)
}

你可以在TS Playground查看输出


TypeScript中的方法重载并不是真的,因为它需要太多的编译器生成的代码,而TS的设计就是为了尽量避免这种情况。方法重载的主要用例可能是为API中有神奇参数的库编写声明。由于处理不同可能的参数集的所有繁重工作都是由您完成的,因此我不认为在每种场景中使用重载而不是特别方法有多大优势。