Überladung der TypeScript-Funktion

241

Abschnitt 6.3 der TypeScript-Sprachspezifikation befasst sich mit der Überladung von Funktionen und gibt konkrete Beispiele für deren Implementierung. Wenn ich jedoch so etwas versuche:

export class LayerFactory { 

    constructor (public styleFactory: Symbology.StyleFactory) { }

    createFeatureLayer (userContext : Model.UserContext, mapWrapperObj : MapWrapperBase) : any {           
         throw "not implemented";
    }                 

    createFeatureLayer(layerName : string, style : any) : any {
        throw "not implemented";
     }        

}

Ich erhalte einen Compilerfehler, der auf eine doppelte Kennung hinweist, obwohl die Funktionsparameter unterschiedlich sind. Selbst wenn ich der zweiten Funktion createFeatureLayer einen zusätzlichen Parameter hinzufüge, wird immer noch ein Compilerfehler angezeigt. Ideen bitte.

Klaus Nji
quelle
Mögliches Duplikat der Methodenüberladung?
BuZZ-dEE

Antworten:

189

Dies kann daran liegen, dass beim Kompilieren beider Funktionen zu JavaScript die Signatur völlig identisch ist. Da JavaScript keine Typen hat, erstellen wir am Ende zwei Funktionen mit der gleichen Anzahl von Argumenten. TypeScript hindert uns also daran, solche Funktionen zu erstellen.

TypeScript unterstützt das Überladen basierend auf der Anzahl der Parameter, aber die zu befolgenden Schritte unterscheiden sich etwas, wenn wir sie mit OO-Sprachen vergleichen. Als Antwort auf eine andere SO-Frage erklärte es jemand mit einem schönen Beispiel: Methodenüberladung? .

Grundsätzlich erstellen wir nur eine Funktion und eine Reihe von Deklarationen, damit TypeScript keine Kompilierungsfehler verursacht. Wenn dieser Code in JavaScript kompiliert wird, ist nur die konkrete Funktion sichtbar. Da eine JavaScript-Funktion durch Übergabe mehrerer Argumente aufgerufen werden kann, funktioniert sie einfach.

S. Ravi Kiran
quelle
50
Die Sprache könnte geändert werden, um dies zu unterstützen. Theoretisch könnte man Funktionsimplementierungen generieren, die von kompiliertem TypeScript (z. B. createFeatureLayer_1 und createFeatureLayer_2) getrennt und aufgerufen werden, und createFeatureLayer könnte dann anhand des Inhalts von Argumenten für die Interaktion mit Vanilla JavaScript bestimmen, welche aufgerufen werden sollen.
Thomas S. Trias
8
Sie sagen es so, als ob eine Überladung in TypeScript nur basierend auf der Anzahl der Parameter möglich ist, während eine Überladung basierend auf dem Typ ebenfalls möglich ist, wie in Steve Fentons Antwort gezeigt.
Matthijs Wessels
9
Das ist irgendwie lahm; TypeScript sollte wirklich die "Meta-Funktion" generieren, die die eindeutig benannte Implementierung entsprechend der Übergabe entsprechend auswählt. Wie es jetzt ist, gibt es einen Riss, in dem Sie den Compiler übergeben könnten, aber Ihre Implementierung des Typs Sniffing könnte falsch sein.
Hesekiel Victor
5
@EzekielVictor TypeScript würde dies tun, wenn es eine zuverlässige Möglichkeit gäbe, Typen zur Laufzeit zu überprüfen.
Dorn
3
Das ist noch komplizierter, es ist mit JavaScript-Typen machbar, aber TS-spezifische Begriffe wie Schnittstellen, types, Aufzählungen, Generika usw. gehen zur Laufzeit verloren. Das ist auch der Grund, warum du es nicht kannst someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling
209

Wenn Sie in TypeScript überladen, haben Sie nur eine Implementierung mit mehreren Signaturen.

class Foo {
    myMethod(a: string);
    myMethod(a: number);
    myMethod(a: number, b: string);
    myMethod(a: any, b?: string) {
        alert(a.toString());
    }
}

Nur die drei Überladungen werden von TypeScript als mögliche Signaturen für einen Methodenaufruf erkannt, nicht die eigentliche Implementierung.

In Ihrem Fall würde ich persönlich zwei Methoden mit unterschiedlichen Namen verwenden, da die Parameter nicht genügend Gemeinsamkeiten aufweisen. Daher ist es wahrscheinlich, dass der Methodenkörper viele "Wenns" benötigt, um zu entscheiden, was zu tun ist.

TypeScript 1.4

Ab TypeScript 1.4 können Sie normalerweise die Notwendigkeit einer Überladung mithilfe eines Union-Typs beseitigen. Das obige Beispiel kann besser ausgedrückt werden mit:

myMethod(a: string | number, b?: string) {
    alert(a.toString());
}

Der Typ von aist "entweder stringoder number".

Fenton
quelle
Gute Antwort. Ich möchte nur hervorheben, dass dies möglicherweise nicht hilfreich ist, wenn versucht wird, aus folgenden Gründen zu überladen: Ich hätte gerne eine Instanz, in der ich mit demselben Konstruktor ein Objekt übergeben kann, das alle erwarteten Eigenschaften definiert, und in die eine Instanz, übergeben einzelne Parameter: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
Im Allgemeinen würde ich eher eine Factory - Methode verwenden , um mir ein Objekt jede Art und Weise zu schaffen - es gibt keine Notwendigkeit, Zweig , wenn Sie anrufen Foo.fromObject(obj)und Foo.fromJson(str)und so weiter.
Fenton
Aber das postuliert, dass man seine Parameter immer entweder als Objekt oder als einzelne Zeichenfolge übergeben wird. Was ist, wenn ich möchte, dass sie separat übergeben werden, wie in meinem vorherigen Kommentar hervorgehoben? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Mir ist auch aufgefallen, dass Sie in der FooKlasse unterschiedliche Methoden haben , fromObject und fromJson?
Hlawuleka MAS
1
Wenn Sie diesem Unterschied bis zur Quelle folgen, werden Sie normalerweise feststellen, dass dies nicht erforderlich ist. Zum Beispiel müssen Sie myNumoder myObjsowieso eingeben, warum also nicht separate Methoden haben und alles klar machen / unnötige Verzweigungslogik vermeiden.
Fenton
2
Beachten Sie, dass die Verwendung eines Union-Typs problematisch sein kann, wenn Sie basierend auf den Parametern unterschiedliche Rückgabetypen haben möchten. Dies kann mit Generika gelöst werden, wenn der Rückgabetyp immer mit einem der Parametertypen übereinstimmt, in anderen Fällen jedoch Überladungen die beste Lösung sind.
John Montgomery
45

Sie können erklären , eine überladene Funktion , indem er erklärt die Funktion als mit einem Typ, der mehr Aufruf Unterschriften hat:

interface IFoo
{
    bar: {
        (s: string): number;
        (n: number): string;
    }
}

Dann folgendes:

var foo1: IFoo = ...;

var n: number = foo1.bar('baz');     // OK
var s: string = foo1.bar(123);       // OK
var a: number[] = foo1.bar([1,2,3]); // ERROR

Die tatsächliche Definition der Funktion muss singulär sein und den entsprechenden internen Versand für ihre Argumente durchführen.

Beispiel: Verwenden einer Klasse (die implementiert werden könnte IFoo, aber nicht muss):

class Foo
{
    public bar(s: string): number;
    public bar(n: number): string;
    public bar(arg: any): any 
    {
        if (typeof(arg) === 'number')
            return arg.toString();
        if (typeof(arg) === 'string')
            return arg.length;
    }
}

Interessant ist hier, dass das anyFormular durch die spezifisch typisierten Überschreibungen ausgeblendet wird.

var foo2: new Foo();

var n: number = foo2.bar('baz');     // OK
var s: string = foo2.bar(123);       // OK
var a: number[] = foo2.bar([1,2,3]); // ERROR
Drew Noakes
quelle
1

Was ist Funktionsüberladung im Allgemeinen?

Überladen von Funktionen oder Verfahren Überlastung ist die Fähigkeit, erstellen mehrere Funktionen des gleichen Namens mit unterschiedlichen Implementierungen ( Wikipedia )


Was ist Funktionsüberladung in JS?

Diese Funktion ist in JS nicht möglich - die zuletzt definierte Funktion wird bei mehreren Deklarationen verwendet:

function foo(a1, a2) { return `${a1}, ${a2}` }
function foo(a1) { return `${a1}` } // replaces above `foo` declaration
foo(42, "foo") // "42"

... und in TS?

Überladungen sind ein Konstrukt zur Kompilierungszeit ohne Auswirkungen auf die JS-Laufzeit:

function foo(s: string): string // overload #1 of foo
function foo(s: string, n: number): number // overload #2 of foo
function foo(s: string, n?: number): string | number {/* ... */} // foo implementation

Ein doppelter Implementierungsfehler wird ausgelöst, wenn Sie den obigen Code verwenden (sicherer als JS). TS wählt die erste passende Überlast in Top-Down-Reihenfolge aus, sodass die Überlastungen von den spezifischsten bis zu den breitesten sortiert werden.


Methodenüberladung in TS: ein komplexeres Beispiel

Überladene Klassenmethodentypen können auf ähnliche Weise wie das Überladen von Funktionen verwendet werden:

class LayerFactory {
    createFeatureLayer(a1: string, a2: number): string
    createFeatureLayer(a1: number, a2: boolean, a3: string): number
    createFeatureLayer(a1: string | number, a2: number | boolean, a3?: string)
        : number | string { /*... your implementation*/ }
}

const fact = new LayerFactory()
fact.createFeatureLayer("foo", 42) // string
fact.createFeatureLayer(3, true, "bar") // number

Die sehr unterschiedlichen Überladungen sind möglich, da die Funktionsimplementierung mit allen vom Compiler erzwungenen Überlastsignaturen kompatibel ist.

Weitere Infos:

ford04
quelle
0

Als Hinweis auf andere habe ich festgestellt, dass Sie, zumindest wie durch TypeScript, das von WebPack für Angular 2 kompiliert wurde, manifestiert werden, stillschweigend überschrieben werden, anstatt überladene Methoden.

myComponent {
  method(): { console.info("no args"); },
  method(arg): { console.info("with arg"); }
}

Berufung:

myComponent.method()

scheint die Methode mit Argumenten auszuführen, wobei die No-Arg-Version stillschweigend ignoriert wird, mit Ausgabe:

with arg
mtyson
quelle
2
Sie können keine separaten Körper für Ihre Überladungen deklarieren, sondern nur unterschiedliche Signaturen.
Adharris
5
Ich bin nicht sicher, welche Version des TypeScript-Compilers Sie verwenden, aber die aktuelle Version gibt eine Duplicate function implementationWarnung für Code wie diesen aus.
Royston Shufflebotham
0

Funktionsüberladung im Typoskript:

Laut Wikipedia (und vielen Programmierbüchern) lautet die Definition der Überladung von Methoden / Funktionen wie folgt:

In einigen Programmiersprachen ist Funktionsüberladung oder Methodenüberladung die Möglichkeit, mehrere gleichnamige Funktionen mit unterschiedlichen Implementierungen zu erstellen . Aufrufe einer überladenen Funktion führen eine bestimmte Implementierung dieser Funktion aus, die dem Kontext des Aufrufs entspricht, sodass ein Funktionsaufruf je nach Kontext unterschiedliche Aufgaben ausführen kann.

In Typoskript können wir keine unterschiedlichen Implementierungen derselben Funktion haben, die entsprechend der Anzahl und Art der Argumente aufgerufen werden. Dies liegt daran, dass beim Kompilieren von TS zu JS die Funktionen in JS die folgenden Eigenschaften aufweisen:

  • JavaScript-Funktionsdefinitionen geben keine Datentypen für ihre Parameter an
  • JavaScript-Funktionen überprüfen beim Aufruf nicht die Anzahl der Argumente

Im engeren Sinne könnte man daher argumentieren, dass es keine Überladung der TS-Funktion gibt. Es gibt jedoch Dinge, die Sie in Ihrem TS-Code tun können, um die Funktionsüberladung perfekt nachzuahmen.

Hier ist ein Beispiel:

function add(a: number, b: number, c: number): number;
function add(a: number, b: number): any;
function add(a: string, b: string): any;

function add(a: any, b: any, c?: any): any {
  if (c) {
    return a + c;
  }
  if (typeof a === 'string') {
    return `a is ${a}, b is ${b}`;
  } else {
    return a + b;
  }
}

In den TS-Dokumenten wird diese Methodenüberladung aufgerufen. Grundsätzlich haben wir dem TS-Compiler mehrere Methodensignaturen (Beschreibungen möglicher Parameter und Typen) bereitgestellt. Jetzt kann TS herausfinden, ob wir unsere Funktion während der Kompilierungszeit korrekt aufgerufen haben, und uns einen Fehler geben, wenn wir die Funktion falsch aufgerufen haben.

Willem van der Veen
quelle