当前位置: 首页 > 知识库问答 >
问题:

TypeScript中的构造函数重载

湛钊
2023-03-14

有人在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一起运行时。它抛出了一个重复的构造函数定义——这是显而易见的。感谢您的帮助。

共有3个答案

范嘉
2023-03-14

听起来您希望对象参数是可选的,并且对象中的每个属性也是可选的。在本例中,如所提供的,不需要重载语法。我想指出一些答案中的错误做法。诚然,它不是基本上编写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(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;
     }
 }

需要添加方法吗?一个冗长但更可扩展的替代方案:上面的类作为接口可以起双重作用,因为它们是相同的。如果选择修改上述类,则需要为传入参数对象定义和引用新接口,因为类不再与传入参数完全相同。请注意,在本例中,表示可选属性的问号(?:)在何处移动。由于我们在类中设置默认值,因此它们保证存在,但在传入参数对象中是可选的:

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

编译后,您将看到如何仅在未定义可选值时使用默认设置;它避免了广泛使用(但容易出错)的回退语法的陷阱,即通过检查无效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;
}());

当设置默认回退值时,请考虑||/或运算符的危险,如其他一些答案所示。下面的代码说明了设置默认值的错误方法。您可以得到意想不到的结果时,评估与Falsey值,如0,",空,未定义,假,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);

在我的测试中,使用es6/typecript去结构化对象可能比O快15-90%bject.assign.使用去结构化参数只允许您分配给对象的方法和属性。例如,考虑这种方法:

class BoxTest {
    public x?: number = 1;

    constructor(params: BoxTest = {} as BoxTest) {
        Object.assign(this, params);
    }
}

如果另一个用户没有使用TypeScript并试图放置一个不属于的参数,比如说,他们可能会尝试放置一个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 params.
console.assert(typeof box.z === 'undefined')
湛玄裳
2023-03-14

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

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

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游乐场查看输出

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

申屠鹏
2023-03-14

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;
    }   
}
 类似资料:
  • 我从我正在使用的库中收到以下接口: 如何创建该类的实现?(我需要一个测试模拟)一个自然的实现,构造函数定义为:

  • null 我不太确定如何处理我的代码,以下是我得到的: 我只是不知道我应该打什么。我相信我已经完成了第一个重载构造函数,但我对此还是新手。 那么,我应该做些什么来使重载构造函数工作呢? 我对Java和面向对象编程非常陌生。

  • TypeScript中非抽象类(非抽象构造函数)的类型签名如下: 这也称为可更新类型。但是,我需要一个抽象类(抽象构造函数)的类型签名。我理解它可以定义为具有类型,但这太宽泛了。难道没有更精确的替代方案吗? 编辑: 为了阐明我的意思,下面的小片段演示了抽象构造函数和非抽象构造函数之间的区别: 类型'typeof实用程序'不能分配给类型'new(... args: any[])= 无法将抽象构造函数

  • 本文向大家介绍C++中构造函数重载,包括了C++中构造函数重载的使用技巧和注意事项,需要的朋友参考一下   当类中没有定义构造函数时,C++编译器自动提供无参构造函数和拷贝构造函数   当类中定义了任意的拷贝构造函数,C++不提供无参构造函数。 系统自动提供的构造函数   无参构造函数      函数体为空   拷贝构造函数      简单的进行成员变量的值复制      Test t1;    

  • 本文向大家介绍解析C++中构造函数的默认参数和构造函数的重载,包括了解析C++中构造函数的默认参数和构造函数的重载的使用技巧和注意事项,需要的朋友参考一下 C++构造函数的默认参数 和普通函数一样,构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。 【例】 程序运行结果为: 程序中对构造函数的定义(第12-16行)也可以改写成参数初始化

  • 为什么在派生类构造函数中调用超类构造函数时id的值为0?当创建子对象时,什么时候在堆中为该对象分配内存?在基类构造函数运行之后还是之前?

  • 让我们分析下面的代码片段: 以上代码不会引发任何错误。我希望它会抛出构造函数调用,在这里我传递5。 似乎构造函数的参数被推断为,其中,静态地很容易发现我将其分配给。 问题是:TypeScript在这里允许,或者换言之,被推断为,而在这种情况下,很明显它必须是一个字符串,这有什么特殊的原因吗? 我知道我可以做另一个这样的类定义: 但这里我指定了参数类型,所以这里没有推断。我也可以这样做: 也不会有任