592 Stimmen

Konstruktorüberladung in TypeScript

Hat jemand Konstruktor Überladen in TypeScript getan. Auf Seite 64 der Sprachspezifikation (v 0.8) gibt es Anweisungen, die Konstruktorüberladungen beschreiben, aber es wurde kein Beispielcode angegeben.

Ich probiere gerade eine ganz einfache Klassendeklaration aus, die so aussieht,

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

Wenn mit tsc BoxSample.ts ausgeführt wird, wirft es eine doppelte Konstruktordefinition aus - was offensichtlich ist. Jede Hilfe ist willkommen.

432voto

chuckj Punkte 25340

TypeScript erlaubt es Ihnen, Überladungen zu deklarieren, aber Sie können nur eine Implementierung haben und diese Implementierung muss eine Signatur haben, die mit allen Überladungen kompatibel ist. In Ihrem Beispiel kann dies leicht mit einem optionalen Parameter wie in erfolgen,

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

oder zwei Überladungen mit einem allgemeineren Konstruktor wie in,

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

Siehe in Spielplatz

221voto

kbtz Punkte 12351

Betreffend Konstruktor-Überladungen Eine gute Alternative wäre, die zusätzlichen Überlastungen als statische Fabrikmethoden . Ich denke, es ist lesbarer und einfacher, als im Konstruktor nach allen möglichen Argumentkombinationen zu suchen.

Im folgenden Beispiel können wir ein Patientenobjekt mit Daten eines Versicherungsträgers erstellen, der die Werte anders speichert. Um eine weitere Datenstruktur für die Instanziierung von Patienten zu unterstützen, könnte man einfach eine weitere statische Methode hinzufügen, um den Standardkonstruktor nach der Normalisierung der bereitgestellten Daten so gut wie möglich aufzurufen.

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

Sie können die Ausgabe überprüfen unter TS Spielplatz


Methodenüberladung in TypeScript ist nicht echt da dies zu viel vom Compiler erzeugten Code erfordern würde und TS darauf ausgelegt ist, dies unter allen Umständen zu vermeiden. Der Hauptanwendungsfall für Methodenüberladung ist wahrscheinlich das Schreiben von Deklarationen für Bibliotheken, die magische Argumente in ihrer API haben. Da alle die schwere Arbeit der Handhabung verschiedener Sätze von möglichen Argumenten von Ihnen getan wird, sehe ich nicht viel Vorteil bei der Verwendung von Überladungen statt ad-hoc-Methoden für jedes Szenario.

106voto

Benson Punkte 3767

Es hört sich so an, als ob Sie möchten, dass der Objektparameter optional ist und auch jede der Eigenschaften des Objekts optional ist. In dem Beispiel ist die Überladungssyntax nicht erforderlich. Ich wollte auf einige schlechte Praktiken in einigen der Antworten hier hinweisen. Zugegeben, es ist nicht der kleinstmögliche Ausdruck, im Wesentlichen zu schreiben box = { x: 0, y: 87, width: 4, height: 0 } aber dies bietet alle Code-Hinweise, die man sich von der beschriebenen Klasse wünschen kann. In diesem Beispiel können Sie eine Funktion mit one, some, all aufrufen, ou keinen der Parameter und erhalten trotzdem Standardwerte.

 /** @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;
     }
 }

Müssen Sie Methoden hinzufügen? Eine ausführliche, aber besser erweiterbare Alternative: Die Box Klasse als Schnittstelle verwendet werden, da sie identisch sind. Wenn Sie die obige Klasse ändern wollen, müssen Sie eine neue Schnittstelle für das Objekt incoming parameters definieren und referenzieren, da die Box Klasse nicht mehr genau wie die Eingangsparameter aussehen würde. Beachten Sie, wo die Fragezeichen ( ?: ), die optionale Eigenschaften bezeichnen, bewegen sich in diesem Fall. Da wir innerhalb der Klasse Standardwerte festlegen, sind sie garantiert vorhanden, aber im Objekt der eingehenden Parameter optional:

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

Unabhängig von der Art und Weise, wie Sie Ihre Klasse definieren, bietet diese Technik die Leitplanken der Typensicherheit, aber auch die Flexibilität, jedes dieser Elemente zu schreiben:

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

Kompiliert sehen Sie, wie die Standardeinstellungen nur verwendet werden, wenn ein optionaler Wert undefiniert ist; dies vermeidet die Fallstricke einer weit verbreiteten (aber fehleranfälligen) Fallback-Syntax von var = isOptional || default; durch Abgleich mit void 0 was eine Abkürzung ist für undefined :

Die kompilierte Ausgabe

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

Addendum: Einstellung von Standardwerten: der falsche Weg

Les || (oder) Betreiber

Bedenken Sie die Gefahr von || /oder Operatoren bei der Festlegung von Standardrückfallwerten, wie in einigen anderen Antworten gezeigt. Der folgende Code veranschaulicht die falsche Art, Standardwerte zu setzen. Sie können unerwartete Ergebnisse erhalten, wenn Sie gegen falsch Werte wie 0, '', null, undefiniert, falsch, 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,params)

In meinen Tests mit es6/typescript destrukturiertes Objekt kann 15-90% schneller sein als Object.assign . Die Verwendung eines destrukturierten Parameters erlaubt nur Methoden und Eigenschaften, die Sie dem Objekt zugewiesen haben. Nehmen wir zum Beispiel diese Methode:

class BoxTest {
    public x?: number = 1;

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

Wenn ein anderer Benutzer kein TypeScript verwendet und versucht, einen Parameter zu platzieren, der nicht dazugehört, könnte er beispielsweise versuchen, ein z Eigenschaft

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

82voto

ShinNoNoir Punkte 2264

Beachten Sie, dass Sie das Fehlen der Überladung auf der Implementierungsebene auch durch Standardparameter in TypeScript umgehen können, z. B.:

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

Editer : Ab 5. Dezember '16, siehe Benson's Antwort für eine ausgefeiltere Lösung, die mehr Flexibilität bietet.

52voto

vegemite4me Punkte 6231

Aktualisierung 2 (28. September 2020): Diese Sprache entwickelt sich ständig weiter, und wenn Sie also in der Lage sind Partial (eingeführt in v2.1) ist dies nun der von mir bevorzugte Weg, dies zu erreichen.

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

   public constructor(b: Partial<Box> = {}) {
      Object.assign(this, b);
   }
}

// Example use
const a = new Box();
const b = new Box({x: 10, height: 99});
const c = new Box({foo: 10});          // Will fail to compile

Aktualisierung (8. Juni 2017): guyarad und snolflake machen in ihren Kommentaren zu meiner Antwort berechtigte Anmerkungen. Ich empfehle den Lesern einen Blick auf die Antworten von Benson , Joe y snolflake die bessere Antworten haben als ich.*

Originalantwort (27. Januar 2014)

Ein weiteres Beispiel dafür, wie man Konstruktorüberladung erreichen kann:

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

Fuente: http://mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X