Gibt es eine Möglichkeit zum Überladen von Methoden in TypeScript?


107

Gibt es eine Möglichkeit, Methoden in TypeScript zu überladen?

Ich möchte so etwas erreichen:

class TestClass {
    someMethod(stringParameter: string): void {
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    someMethod(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }
}

var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");

Hier ist ein Beispiel dafür, was ich nicht tun möchte (ich hasse diesen Teil des Überladungshacks in JS wirklich):

class TestClass {
    private someMethod_Overload_string(stringParameter: string): void {
        // A lot of code could be here... I don't want to mix it with switch or if statement in general function
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    private someMethod_Overload_number_string(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }

    private someMethod_Overload_string_number(stringParameter: string, numberParameter: number): void {
        alert("Variant #3: stringParameter = " + stringParameter + ", numberParameter = " + numberParameter);
    }

    public someMethod(stringParameter: string): void;
    public someMethod(numberParameter: number, stringParameter: string): void;
    public someMethod(stringParameter: string, numberParameter: number): void;

    public someMethod(): void {
        switch (arguments.length) {
        case 1:
            if(typeof arguments[0] == "string") {
                this.someMethod_Overload_string(arguments[0]);
                return;
            }
            return; // Unreachable area for this case, unnecessary return statement
        case 2:
            if ((typeof arguments[0] == "number") &&
                (typeof arguments[1] == "string")) {
                this.someMethod_Overload_number_string(arguments[0], arguments[1]);
            }
            else if ((typeof arguments[0] == "string") &&
                     (typeof arguments[1] == "number")) {
                this.someMethod_Overload_string_number(arguments[0], arguments[1]);
            }
            return; // Unreachable area for this case, unnecessary return statement
        }
    }
}


var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");
testClass.someMethod("string for v#3", 54321);

1
Ja, es besteht die Möglichkeit, dass die Sprache noch nicht tot ist. Noch Fragen?
hakre

6
@hakre Das ist eine seltsame Sache, wenn man bedenkt, dass TypeScript bereits das Überladen von Methoden unterstützt.
Svick

@svick: nennst du diese Methode Überladung? In Ihrer Antwort ist die Methode selbst nicht überlastet, ein Körper ist in der Nähe.
hakre

2
@hakre Die Spezifikation nennt es Methodenüberladung. Sie können sicherlich argumentieren, dass es keine besonders schöne Version davon ist, aber ich denke, Sie können nicht sagen, dass es überhaupt nicht existiert.
Svick

@svick: Das habe ich auch nicht gesagt. Aber es scheint mir, dass die Chancen, nach denen OP fragt, spezifisch für das mentale Modell der Methodenüberladung sind. Für die Haarspalterei könnte man sagen, dass es sich um eine Überladung der Methodensignatur handelt;)
hakre

Antworten:


164

Gemäß der Spezifikation unterstützt TypeScript das Überladen von Methoden, ist jedoch recht umständlich und enthält viele manuelle Arbeiten zur Überprüfung der Parametertypen. Ich denke, das liegt hauptsächlich daran, dass das Überladen von Methoden in einfachem JavaScript am nächsten kommt, wenn auch überprüft wird und TypeScript versucht, die tatsächlichen Methodenkörper nicht zu ändern, um unnötige Kosten für die Laufzeitleistung zu vermeiden.

Wenn ich es richtig verstehe, müssen Sie zuerst eine Methodendeklaration für jede der Überladungen und dann eine Methodenimplementierung schreiben , die ihre Argumente überprüft, um zu entscheiden, welche Überladung aufgerufen wurde. Die Signatur der Implementierung muss mit allen Überlastungen kompatibel sein.

class TestClass {
    someMethod(stringParameter: string): void;
    someMethod(numberParameter: number, stringParameter: string): void;

    someMethod(stringOrNumberParameter: any, stringParameter?: string): void {
        if (stringOrNumberParameter && typeof stringOrNumberParameter == "number")
            alert("Variant #2: numberParameter = " + stringOrNumberParameter + ", stringParameter = " + stringParameter);
        else
            alert("Variant #1: stringParameter = " + stringOrNumberParameter);
    }
}

3
@NicoVanBelle JavaScript unterstützt das Überladen von Methoden überhaupt nicht, oder? Wie kann die Rückkehr zu JS helfen?
Svick

Was ist mit der Überprüfung von Schnittstellen? Haben Sie eine bessere Lösung als diese: stackoverflow.com/questions/14425568/… ?
DiPix

hmm .. ich mag das nicht wirklich, nur einen optionalen Parameter zu haben ist besser zu lesen.
LuckyLikey

3
Ich denke, was hier wichtig ist, ist, dass Ihr Code ohne jede Menge typechecking if-Anweisungen lesbar bleibt. Wen interessiert es, in was es sich verwandelt?
Brain2000

34

Aus Gründen der Übersichtlichkeit aktualisieren. Das Überladen von Methoden in TypeScript ist insofern eine nützliche Funktion, als Sie Typdefinitionen für vorhandene Bibliotheken mit einer API erstellen können, die dargestellt werden muss.

Wenn Sie jedoch Ihren eigenen Code schreiben, können Sie möglicherweise den kognitiven Overhead von Überladungen mithilfe optionaler oder Standardparameter vermeiden. Dies ist die besser lesbare Alternative zu Methodenüberladungen und hält Ihre API ehrlich, da Sie vermeiden, Überladungen mit nicht intuitiver Reihenfolge zu erstellen.

Das allgemeine Gesetz für TypeScript-Überladungen lautet:

Wenn Sie die Überladungssignaturen löschen können und alle Ihre Tests erfolgreich sind, benötigen Sie keine TypeScript-Überladungen

Normalerweise können Sie dasselbe mit optionalen oder Standardparametern erreichen - oder mit Vereinigungstypen oder mit etwas Objektorientierung.

Die eigentliche Frage

Die eigentliche Frage verlangt nach einer Überlastung von:

someMethod(stringParameter: string): void {

someMethod(numberParameter: number, stringParameter: string): void {

Selbst in Sprachen, die Überladungen mit separaten Implementierungen unterstützen (Hinweis: TypeScript-Überladungen teilen sich eine einzige Implementierung), empfehlen Programmierer, eine einheitliche Reihenfolge zu gewährleisten. Dies würde die Unterschriften machen:

someMethod(stringParameter: string): void {

someMethod(stringParameter: string, numberParameter: number): void {

Das stringParameterist immer erforderlich, also geht es zuerst. Sie könnten dies als funktionierende TypeScript-Überladung schreiben:

someMethod(stringParameter: string): void;
someMethod(stringParameter: string, numberParameter: number): void;
someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

Nach dem Gesetz der TypeScript-Überladungen können wir die Überladungssignaturen löschen und alle unsere Tests werden weiterhin bestanden.

someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

Die tatsächliche Frage in der tatsächlichen Reihenfolge

Wenn Sie entschlossen wären, die ursprüngliche Bestellung beizubehalten, wären die Überlastungen:

someMethod(stringParameter: string): void;
someMethod(numberParameter: number, stringParameter: string): void;
someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

Das ist eine Menge Verzweigung, um herauszufinden, wo die Parameter abgelegt werden sollen, aber Sie wollten diese Reihenfolge wirklich beibehalten, wenn Sie so weit lesen ... aber warten Sie, was passiert, wenn wir das Gesetz der TypeScript-Überladungen anwenden?

someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

Genug Verzweigung schon

Angesichts der Menge an Typprüfungen, die wir durchführen müssen, ist die beste Antwort vielleicht einfach, zwei Methoden zu haben:

someMethod(stringParameter: string): void {
  this.someOtherMethod(0, stringParameter);
}

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}

Das ist allgemein nicht als Methodenüberladung bekannt. Siehe auch die Frage, der Typ des ersten Parameters ändert sich.
hakre

3
Ich habe in meiner Antwort bestätigt, dass Sie den optionalen Parameter als letzten setzen würden, sodass der numberParameter das zweite Argument und optional wäre. TypeScript unterstützt keine "richtigen" Methodenüberladungen - aber selbst die C # -Welt bewegt sich weg von Überladungen hin zu optionalen Parametern, da dies in vielen Fällen zu besser lesbarem Code führt.
Fenton

Du meinst if (typeof numberParameter != 'undefined'), richtig;)
Juan Mendes

Dies hängt von Ihrem Anwendungsfall ab, insbesondere davon, ob Sie Nullen akzeptieren. Wenn Sie es tun müssen, denken Sie daran, es zu verwenden !==, um Jonglieren zu vermeiden.
Fenton

1
@Sebastian, das klingt nach einer Möglichkeit zur Abhängigkeitsinjektion. Angesichts der Tatsache, dass das Überladen von Methoden in TypeScript eine einzelne Methode mit mehreren Dekorationen, Standardparametern und Vereinigungstypen umfasst, wird eine bessere Erfahrung erzielt. Wenn sich die Methoden wesentlich unterscheiden, verwenden Sie entweder eine Abstraktion oder implementieren mehrere Methoden.
Fenton

7

Ich wünsche. Ich möchte diese Funktion auch, aber TypeScript muss mit untypisiertem JavaScript kompatibel sein, das keine überladenen Methoden hat. dh Wenn Ihre überladene Methode von JavaScript aufgerufen wird, kann sie nur an eine Methodenimplementierung gesendet werden.

Es gibt nur wenige relevante Diskussionen über Codeplex. z.B

https://typescript.codeplex.com/workitem/617

Ich denke immer noch, dass TypeScript das gesamte If'ing und Switching generieren sollte, damit wir es nicht tun müssen.


2

Warum nicht die optionale eigenschaftsdefinierte Schnittstelle als Funktionsargument verwenden?

Für den Fall in dieser Frage kann die Verwendung einer Inline-Schnittstelle, die nur mit einigen optionalen Eigenschaften definiert ist, direkt Code wie folgt erzeugen:

class TestClass {

    someMethod(arg: { stringParameter: string, numberParameter?: number }): void {
        let numberParameterMsg = "Variant #1:";
        if (arg.numberParameter) {
            numberParameterMsg = `Variant #2: numberParameter = ${arg.numberParameter},`;
        }
        alert(`${numberParameterMsg} stringParameter = ${arg.stringParameter}`);
    }
}

var testClass = new TestClass();
testClass.someMethod({ stringParameter: "string for v#1" });
testClass.someMethod({ numberParameter: 12345, stringParameter: "string for v#2" });

Da das in TypeScript bereitgestellte Überladen, wie in den Kommentaren anderer erwähnt, nur eine Liste der verschiedenen Signaturen der Funktion ist, ohne entsprechende Implementierungscodes wie andere statische Sprachen zu unterstützen. Die Implementierung muss also immer noch in nur einem Funktionskörper erfolgen, was die Verwendung der Funktionsüberladung in Typescript nicht so komfortabel macht wie solche Sprachen, die die eigentliche Überladungsfunktion unterstützen.

Es gibt jedoch immer noch viele neue und praktische Dinge in Typoskript, die nicht in der Legacy-Programmiersprache verfügbar sind, wo die optionale Eigenschaftsunterstützung in einer anonymen Schnittstelle ein solcher Ansatz ist, um die komfortable Zone aufgrund der Überlastung der Legacy-Funktionen zu erfüllen, denke ich.


0

Wenn es viele Variationen von Methodenüberladungen gibt, besteht eine andere Möglichkeit darin, eine Klasse mit all Ihren Argumenten zu erstellen. Sie können also nur die gewünschten Parameter in beliebiger Reihenfolge übergeben.

class SomeMethodConfig {
  stringParameter: string;
  numberParameter: number;

 /**
 *
 */
 constructor(stringParameter: string = '012', numberParameter?: number) { // different ways to make a param optional
   this.numberParameter = 456; // you can put a default value here
   this.stringParameter = stringParameter; // to pass a value throw the constructor if necessary
 }
}

Sie können auch einen Konstruktor mit Standardwerten erstellen und / oder einige obligatorische Argumente übergeben. Dann benutze einfach so:

const config = new SomeMethodConfig('text');
config.numberParameter = 123; // initialize an optional parameter only if you want to do it
this.SomeMethod(config);

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.