C'è un modo per eseguire il sovraccarico del metodo in TypeScript?


109

C'è un modo per eseguire il sovraccarico del metodo nel linguaggio TypeScript?

Voglio ottenere qualcosa del genere:

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

Ecco un esempio di ciò che non voglio fare (odio davvero quella parte del sovraccarico di hack in JS):

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
Sì, c'è una possibilità, la lingua non è ancora morta. Altre domande?
hakre

6
@hakre È una cosa strana da dire, considerando che TypeScript supporta già il sovraccarico dei metodi.
svick

@svick: beh, chiami quel metodo sovraccarico? Nella tua risposta il metodo stesso non è sovraccarico, un corpo se ne sta in giro.
hakre

2
@hakre La specifica lo chiama sovraccarico del metodo. Puoi certamente sostenere che non è una versione particolarmente bella, ma penso che non si possa dire che non esiste affatto.
svick

@svick: non l'ho detto neanche io. Ma mi sembra che le possibilità che OP chiede siano specifiche sul modello mentale del sovraccarico del metodo. Per la
rottura

Risposte:


165

Secondo le specifiche, TypeScript supporta il sovraccarico del metodo, ma è piuttosto scomodo e include un sacco di lavoro manuale che controlla i tipi di parametri. Penso che sia principalmente perché il più vicino possibile al sovraccarico del metodo in JavaScript semplice include anche il controllo e TypeScript cerca di non modificare i corpi dei metodi effettivi per evitare costi di prestazioni di runtime non necessari.

Se ho capito bene, si deve prima scrivere una dichiarazione di metodo per ciascuno dei sovraccarichi e quindi un'implementazione del metodo che controlla i suoi argomenti per decidere che il sovraccarico è stato chiamato. La firma dell'implementazione deve essere compatibile con tutti i sovraccarichi.

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 non supporta affatto il sovraccarico del metodo, giusto? Quindi come tornerà a JS aiuterà?
svick

E il controllo delle interfacce? Hai una soluzione migliore di questa: stackoverflow.com/questions/14425568/… ?
DiPix

hmm .. non mi piace molto, avere solo un parametro opzionale invece è meglio leggere.
LuckyLikey

3
Penso che ciò che è importante qui sia che mantenga il tuo codice leggibile senza un sacco di istruzioni if. A chi importa in cosa trasporta.
Brain2000

34

Aggiornamento per chiarezza. Il sovraccarico dei metodi in TypeScript è una funzionalità utile in quanto consente di creare definizioni di tipo per le librerie esistenti con un'API che deve essere rappresentata.

Quando scrivi il tuo codice, tuttavia, potresti essere in grado di evitare il sovraccarico cognitivo dei sovraccarichi utilizzando parametri opzionali o predefiniti. Questa è l'alternativa più leggibile agli overload del metodo e mantiene anche la tua API onesta poiché eviterai di creare overload con un ordinamento non intuitivo.

La legge generale dei sovraccarichi TypeScript è:

Se è possibile eliminare le firme di sovraccarico e tutti i test vengono superati, non sono necessari gli overload di TypeScript

Di solito puoi ottenere la stessa cosa con parametri opzionali o predefiniti, o con i tipi di unione o con un po 'di orientamento agli oggetti.

La vera domanda

La domanda attuale richiede un sovraccarico di:

someMethod(stringParameter: string): void {

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

Ora anche nei linguaggi che supportano gli overload con implementazioni separate (nota: gli overload di TypeScript condividono un'unica implementazione) - i programmatori sono consigli per fornire coerenza nell'ordine. Questo farebbe le firme:

someMethod(stringParameter: string): void {

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

Il stringParameterè sempre necessario, così si va per primo. Potresti scrivere questo come un sovraccarico di TypeScript funzionante:

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

Ma seguendo la legge degli overload di TypeScript, possiamo eliminare le firme di sovraccarico e tutti i nostri test verranno comunque superati.

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

La domanda effettiva, nell'ordine effettivo

Se fossi determinato a persistere con l'ordine originale, i sovraccarichi sarebbero:

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

Ora ci sono molte ramificazioni per capire dove mettere i parametri, ma volevi davvero preservare questo ordine se stai leggendo così lontano ... ma aspetta, cosa succede se applichiamo la legge dei sovraccarichi TypeScript?

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

Già abbastanza ramificazioni

Naturalmente, data la quantità di controllo del tipo che dobbiamo fare ... forse la risposta migliore è semplicemente avere due metodi:

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

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

questo non è comunemente noto come sovraccarico del metodo. vedi anche la domanda, il tipo del primo parametro cambia.
hakre

3
Ho riconosciuto che nella mia risposta avresti messo il parametro opzionale per ultimo, quindi il numberparametro sarebbe il secondo argomento e sarebbe opzionale. TypeScript non supporta gli overload del metodo "corretto", ma anche il mondo di C # si sta allontanando dagli overload verso parametri opzionali poiché in molti casi porta a codice più leggibile.
Fenton

Vuoi dire if (typeof numberParameter != 'undefined'), giusto;)
Juan Mendes

Dipende dal tuo caso d'uso, in particolare se accetti gli zeri. Se devi farlo, ricordati di usarlo !==per evitare di fare giocolieri.
Fenton

1
@Sebastian che suona come un'opportunità per l'iniezione di dipendenza. Dato che il sovraccarico del metodo in TypeScript implica un singolo metodo con più decorazioni, parametri predefiniti e tipi di unione, fornire un'esperienza migliore. Se i metodi differiscono in modi più sostanziali, puoi utilizzare un'astrazione o implementare più metodi.
Fenton

7

Spero che. Voglio anche questa funzione, ma TypeScript deve essere interoperabile con JavaScript non tipizzato che non ha metodi sovraccaricati. Ad esempio, se il metodo sovraccarico viene chiamato da JavaScript, può essere inviato solo all'implementazione di un metodo.

Ci sono alcune discussioni rilevanti su codeplex. per esempio

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

Continuo a pensare che TypeScript dovrebbe generare tutti gli if'ing e lo switching, quindi non avremmo bisogno di farlo.


2

Perché non utilizzare l'interfaccia definita dalla proprietà opzionale come argomento della funzione ..

Per il caso in questa domanda, l'utilizzo di un'interfaccia inline definita con alcune proprietà opzionali potrebbe creare direttamente codice come qualcosa di seguito:

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

Perché il sovraccarico fornito in TypeScript è, come menzionato nei commenti di altri, solo un elenco delle diverse firme della funzione senza supportare i codici di implementazione corrispondenti come altri linguaggi statici. Quindi l'implementazione deve ancora essere eseguita in un solo corpo della funzione, il che rende l'uso del sovraccarico di funzioni in Typescript non così comodo come i linguaggi che supportano la funzione di sovraccarico reale.

Tuttavia, ci sono ancora molte cose nuove e convenienti fornite in dattiloscritto che non sono disponibili nel linguaggio di programmazione legacy, dove il supporto di proprietà opzionali in un'interfaccia anonima è un tale approccio per soddisfare la zona confortevole dal sovraccarico della funzione legacy, penso.


0

Se ci sono molte varianti degli overload del metodo, un altro modo è semplicemente creare una classe con tutti i tuoi argomenti all'interno. Quindi puoi passare solo il parametro che desideri in qualsiasi ordine.

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

Inoltre è possibile creare un costruttore con valori predefiniti e / o passare alcuni argomenti obbligatori. Quindi usa semplicemente in questo modo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.