首页 文章

TypeScript中的构造函数重载

提问于
浏览
259

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

我现在正在尝试一个非常基本的课堂宣言;它看起来像这样,

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运行时,它会抛出一个重复的构造函数定义 - 这很明显 . 任何帮助表示赞赏 .

10 回答

  • 64

    你应该记住......

    contructor()
    
    constructor(a:any, b:any, c:any)
    

    它与 new()new("a","b","c") 相同

    从而

    constructor(a?:any, b?:any, c?:any)
    

    是同样的,更灵活......

    new()new("a")new("a","b")new("a","b","c")

  • 71

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

    请注意,您还可以通过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日起,请参阅Benson's answer以获得更精细的解决方案,从而提供更大的灵活性 .

  • 3

    关于 constructor overloads ,一种替代方案是将额外的重载实现为 static factory methods . 我认为它比测试你的调用参数更具可读性和更少混淆 . 这是一个简单的例子:

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

    TypeScript中的方法重载不是真的,让's say, as it would require too much compiler-generated code and the core team try to avoid that at all costs. Currently the main reason for method overloading to be present on the language is to provide a way to write declarations for libraries with magic arguments in their API. Since you' ll需要自己完成所有繁重工作以处理不同的参数集我在使用重载而不是分离的方法时看不到多少优势 .

  • 20

    注意:这已经过简化并在4/13/2017更新以反映TypeScript 2.1,请参阅TypeScript 1.8答案的历史记录 .

    听起来您希望object参数是可选的,并且对象中的每个属性都是可选的 . 在示例中,如提供的那样,重载语法isn 't needed. I wanted to point out some bad practices in the some of the answers here. Granted, it'不是本质上写入 box = { x: 0, y: 87, width: 4, height: 0 } 的最小可能表达式,但是这提供了所有可能想要从类中描述的细节的代码 . 此示例允许您使用一个,一些,全部或没有参数调用函数,并仍然获取默认值 .

    /** @class */
     class Box {
         public x?: number;
         public y?: number;
         public height?: number;
         public width?: number;     
    
         // The class can work double-duty as the interface here since they are identical
         // Alternately, reference your own interface, e.g.:  `...BoxI = {} as BoxI` 
         constructor(obj: Box = {} as Box) {
    
             // Define the properties of the incoming `obj` object here. 
             // Setting a default value with the `= 0` syntax is optional for each parameter
             let {
                 x = 0,
                 y = 0,
                 height = 0,
                 width = 0
             } = obj;
    
             //  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;
         }
     }
    

    这是一种非常安全的方法来编写可能没有定义对象的所有属性的参数 . 您现在可以安全地编写以下任何内容:

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

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

    编译输出

    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(this,obj)

    在我的测试中,使用es6 / typescript destructured object can be almost 90% faster than Object.assign . 使用destructured参数只允许您为对象分配的方法和属性 . 例如,考虑这种方法:

    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')
    
  • 53

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

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

    Update (8 June 2017): guyarad和snolflake在下面的评论中给出了有效的答案 . 我建议读者查看BensonJoesnolflake的答案,他们的答案比我的答案要好 .

    Original Answer (27 January 2014)

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

    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

  • 0

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

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

    请记住,如果未在 Track 上定义,则将分配在 track 中传递的所有属性 .

  • -4

    另一个版本喜欢@ 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;
        }
    }
    
  • 220

    您可以通过以下方式处理:

    import { assign } from 'lodash'; // if you don't have lodash use Object.assign
    class Box {
        x: number;
        y: number;
        height: number;
        width: number;
        constructor(obj: Partial<Box> = {}) {    
             assign(this, obj);
        }
    }
    

    部分将使您的字段(x,y,height,width)可选,允许多个构造函数

    例如:你可以做没有高度和宽度的 new Box({x,y}) .

    = {} 将处理假值,如undefined,null等,然后你可以做 new Box()

相关问题