Existe-t-il un moyen de surcharger les méthodes dans TypeScript?


109

Existe-t-il un moyen de surcharger les méthodes en langage TypeScript?

Je veux réaliser quelque chose comme ceci:

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

Voici un exemple de ce que je ne veux pas faire (je déteste vraiment cette partie de la surcharge de hack dans 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
Oui, il y a une chance, la langue n'est pas encore morte. D'autres questions?
hakre

6
@hakre C'est une chose étrange à dire, étant donné que TypeScript prend déjà en charge la surcharge de méthodes.
svick

@svick: eh bien, appelez-vous cette méthode de surcharge? Dans votre réponse, la méthode elle-même n'est pas surchargée, un seul corps se promène.
hakre

2
@hakre La spécification l'appelle surcharge de méthode. Vous pouvez certainement affirmer que ce n'est pas une version particulièrement agréable de celui-ci, mais je pense que vous ne pouvez pas dire qu'il n'existe pas du tout.
svick

@svick: Je n'ai pas dit non plus. Mais il me semble que les chances qu'OP interroge sont spécifiques au modèle mental de la surcharge de méthodes. Pour le partage des cheveux, nous pourrions dire que c'est une surcharge de signature de méthode;)
hakre

Réponses:


165

Selon la spécification, TypeScript prend en charge la surcharge de méthode, mais c'est assez gênant et inclut beaucoup de travail manuel de vérification des types de paramètres. Je pense que c'est principalement parce que le plus proche de la surcharge de méthode en JavaScript brut inclut également cette vérification et TypeScript essaie de ne pas modifier les corps de méthode réels pour éviter tout coût d'exécution inutile.

Si je comprends bien, vous devez d'abord écrire une déclaration de méthode pour chacune des surcharges, puis une implémentation de méthode qui vérifie ses arguments pour décider quelle surcharge a été appelée. La signature de l'implémentation doit être compatible avec toutes les surcharges.

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 ne prend pas du tout en charge la surcharge de méthode, non? Alors, comment revenir à JS va-t-il aider?
svick le

Et la vérification des interfaces? Avez-vous une meilleure solution que cela: stackoverflow.com/questions/14425568/... ?
DiPix

hmm .. je n'aime pas vraiment ça, juste avoir un paramètre optionnel à la place est mieux à lire.
LuckyLikey

3
Je pense que ce qui est important ici, c'est qu'il garde votre code lisible sans une tonne de vérifications de type if. Qui se soucie de ce que ça transpile.
Brain2000

34

Mettez à jour pour plus de clarté. La surcharge de méthode dans TypeScript est une fonctionnalité utile dans la mesure où elle vous permet de créer des définitions de type pour des bibliothèques existantes avec une API qui doit être représentée.

Cependant, lors de l'écriture de votre propre code, vous pourrez peut-être éviter la surcharge cognitive des surcharges en utilisant des paramètres facultatifs ou par défaut. C'est l'alternative la plus lisible aux surcharges de méthodes et maintient également votre API honnête car vous éviterez de créer des surcharges avec un ordre non intuitif.

La loi générale des surcharges TypeScript est:

Si vous pouvez supprimer les signatures de surcharge et que tous vos tests réussissent, vous n'avez pas besoin de surcharges TypeScript

Vous pouvez généralement réaliser la même chose avec des paramètres optionnels ou par défaut - ou avec des types d'union, ou avec un peu d'orientation objet.

La vraie question

La question réelle demande une surcharge de:

someMethod(stringParameter: string): void {

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

Maintenant, même dans les langages qui prennent en charge les surcharges avec des implémentations séparées (note: les surcharges TypeScript partagent une seule implémentation) - les programmeurs sont des conseils pour assurer la cohérence dans l'ordre. Cela rendrait les signatures:

someMethod(stringParameter: string): void {

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

Le stringParameterest toujours nécessaire, donc ça passe en premier. Vous pouvez écrire ceci comme une surcharge TypeScript de travail:

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

Mais en suivant la loi des surcharges TypeScript, nous pouvons supprimer les signatures de surcharge et tous nos tests réussiront toujours.

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

La question réelle, dans l'ordre réel

Si vous étiez déterminé à conserver la commande d'origine, les surcharges seraient:

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

Maintenant, c'est beaucoup de branchements pour déterminer où mettre les paramètres, mais vous vouliez vraiment conserver cet ordre si vous lisez jusqu'ici ... mais attendez, que se passe-t-il si nous appliquons la loi des surcharges 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;
    }
  }
}

Assez de branchement déjà

Bien sûr, étant donné la quantité de vérification de type que nous devons faire ... peut-être que la meilleure réponse est simplement d'avoir deux méthodes:

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

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

qui n'est généralement pas connue sous le nom de surcharge de méthode. voir aussi la question, le type du premier paramètre change.
hakre

3
J'ai reconnu que dans ma réponse - vous mettriez le paramètre facultatif en dernier, donc le numberparamètre serait le deuxième argument et serait facultatif. TypeScript ne prend pas en charge les surcharges de méthodes «appropriées» - mais même le monde C # s'éloigne des surcharges vers des paramètres optionnels car dans de nombreux cas, cela conduit à un code plus lisible.
Fenton

Vous voulez dire if (typeof numberParameter != 'undefined'), à droite;)
Juan Mendes

Cela dépend de votre cas d'utilisation, en particulier si vous acceptez les zéros. Si vous devez le faire, pensez à l'utiliser !==pour éviter de jongler.
Fenton

1
@Sebastian qui ressemble à une opportunité d'injection de dépendances. Étant donné que la surcharge de méthode dans TypeScript implique une seule méthode avec plusieurs décorations, paramètres par défaut et types d'union, offre une meilleure expérience. Si les méthodes diffèrent de manière plus substantielle, vous utilisez une abstraction ou implémentez plusieurs méthodes.
Fenton

7

Je souhaite que. Je veux aussi cette fonctionnalité, mais TypeScript doit être interopérable avec JavaScript non typé qui n'a pas de méthodes surchargées. c'est-à-dire que si votre méthode surchargée est appelée à partir de JavaScript, elle ne peut être envoyée qu'à une seule implémentation de méthode.

Il y a quelques discussions pertinentes sur le codeplex. par exemple

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

Je pense toujours que TypeScript devrait générer tous les if'ing et la commutation afin que nous n'ayons pas besoin de le faire.


2

Pourquoi ne pas utiliser l' interface définie par la propriété facultative comme argument de fonction.

Pour le cas de cette question, l'utilisation d'une interface en ligne définie avec certaines propriétés facultatives uniquement pourrait créer directement du code comme ci-dessous:

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

Parce que la surcharge fournie dans TypeScript est, comme mentionné dans les commentaires des autres, juste une liste des différentes signatures de fonction sans prendre en charge les codes d'implémentation correspondants comme les autres langages statiques. Donc, l'implémentation doit toujours être effectuée dans un seul corps de fonction, ce qui rend l'utilisation de la surcharge de fonction dans Typescript pas aussi confortable que de tels langages prenant en charge la fonctionnalité de surcharge réelle.

Cependant, il existe encore de nombreux éléments nouveaux et pratiques fournis en tapuscrit qui ne sont pas disponibles dans le langage de programmation hérité, où la prise en charge facultative des propriétés dans une interface anonyme est une telle approche pour répondre à la zone confortable de la surcharge des fonctions héritées, je pense.


0

S'il y a beaucoup de variations de surcharge de méthode, une autre façon est simplement de créer une classe avec tous vos arguments à l'intérieur. Vous ne pouvez donc transmettre que les paramètres de votre choix dans n'importe quel ordre.

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

Vous pouvez également créer un constructeur avec des valeurs par défaut et / ou passer des arguments obligatoires. Ensuite, utilisez simplement comme ceci:

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.