Surcharge de la fonction TypeScript

244

La section 6.3 de la spécification du langage TypeScript parle de surcharge de fonctions et donne des exemples concrets sur la façon de l'implémenter. Cependant, si j'essaye quelque chose comme ça:

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

}

J'obtiens une erreur du compilateur indiquant l'identificateur en double même si les paramètres de fonction sont de types différents. Même si j'ajoute un paramètre supplémentaire à la deuxième fonction createFeatureLayer, j'obtiens toujours une erreur de compilation. Des idées, s'il vous plaît.

Klaus Nji
la source
Duplication possible de surcharge
BuZZ-dEE

Réponses:

189

Cela peut être dû au fait que, lorsque les deux fonctions sont compilées en JavaScript, leur signature est totalement identique. Comme JavaScript n'a pas de types, nous finissons par créer deux fonctions en prenant le même nombre d'arguments. Ainsi, TypeScript nous empêche de créer de telles fonctions.

TypeScript prend en charge la surcharge en fonction du nombre de paramètres, mais les étapes à suivre sont un peu différentes si l'on compare aux langages OO. En réponse à une autre question SO, quelqu'un l'a expliqué avec un bel exemple: surcharge de méthode? .

Fondamentalement, ce que nous faisons, c'est que nous créons une seule fonction et un certain nombre de déclarations afin que TypeScript ne donne pas d'erreurs de compilation. Lorsque ce code est compilé en JavaScript, la fonction concrète seule sera visible. Comme une fonction JavaScript peut être appelée en passant plusieurs arguments, cela fonctionne simplement.

S. Ravi Kiran
la source
50
Le libellé pourrait être modifié pour appuyer cela. En théorie, on pourrait générer des implémentations de fonctions qui sont nommées séparément et appelées par TypeScript compilé (par exemple createFeatureLayer_1 et createFeatureLayer_2) et createFeatureLayer pourrait alors déterminer laquelle appeler en fonction du contenu des arguments pour l'interopérabilité avec JavaScript vanilla.
Thomas S. Trias
8
Vous le dites comme si la surcharge en TypeScript n'est possible qu'en fonction du nombre de paramètres, tandis que la surcharge en fonction du type est également possible, comme indiqué dans la réponse de Steve Fenton.
Matthijs Wessels
9
C'est un peu boiteux; TypeScript devrait vraiment générer la "fonction méta" qui choisit l'implémentation au nom unique de manière appropriée en fonction de ce qu'elle a été transmise. Comment c'est maintenant il y a une faille où vous pourriez passer le compilateur mais votre implémentation du type reniflant pourrait être incorrecte.
Ezekiel Victor
5
@EzekielVictor TypeScript le ferait s'il existait un moyen fiable de vérifier les types au moment de l'exécution.
thorn̈
3
C'est encore plus compliqué, c'est faisable avec les types de JavaScript, mais les notions spécifiques à TS comme les interfaces, les types, les énumérations, les génériques, etc., sont perdues au moment de l'exécution. C'est aussi pourquoi vous ne pouvez pas le faire someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling
209

Lorsque vous surchargez dans TypeScript, vous n'avez qu'une seule implémentation avec plusieurs signatures.

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

Seules les trois surcharges sont reconnues par TypeScript comme des signatures possibles pour un appel de méthode, pas l'implémentation réelle.

Dans votre cas, j'utiliserais personnellement deux méthodes avec des noms différents car il n'y a pas suffisamment de points communs dans les paramètres, ce qui rend probable que le corps de la méthode devra avoir beaucoup de "si" pour décider quoi faire.

TypeScript 1.4

À partir de TypeScript 1.4, vous pouvez généralement supprimer la nécessité d'une surcharge à l'aide d'un type d'union. L'exemple ci-dessus peut être mieux exprimé en utilisant:

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

Le type de aest "soit stringou number".

Fenton
la source
Très bonne réponse. Je voudrais juste souligner que cela pourrait ne pas être utile lorsque l'on essaie de surcharger pour des raisons telles que: je voudrais avoir une instance, où en utilisant le même constructeur, je peux passer un objet définissant toutes les propriétés attendues et dans la seule instance, passer les paramètres individuels: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
En général, je préfère utiliser une méthode de fabrication pour me créer un objet dans chaque sens - il n'y a pas besoin de branche si vous appelez Foo.fromObject(obj)et Foo.fromJson(str)et ainsi de suite.
Fenton
Mais cela postule que l'on passera toujours leurs paramètres sous forme d'objet ou de chaîne unique, que se passe-t-il si je veux les faire passer séparément, comme souligné dans mon commentaire précédent? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) J'ai également remarqué que vous avez différentes méthodes dans la Fooclasse, fromObject et fromJson?
Hlawuleka MAS
1
Si vous suivez cette différence jusqu'à la source, vous constaterez généralement qu'elle n'est pas nécessaire. Par exemple, vous devez taper myNumou de myObjtoute façon, alors pourquoi ne pas avoir des méthodes distinctes et clarifier tout / éviter la logique de branchement inutile.
Fenton
2
Notez que l'utilisation d'un type d'union peut être problématique si vous souhaitez avoir différents types de retour en fonction des paramètres. Cela peut être résolu avec des génériques si le type de retour correspond toujours à l'un des types de paramètres, mais dans d'autres cas, les surcharges sont la meilleure solution.
John Montgomery
45

Vous pouvez déclarer une fonction surchargée en déclarant la fonction comme ayant un type qui a plusieurs signatures d'invocation:

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

Ensuite, ce qui suit:

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

La définition réelle de la fonction doit être singulière et effectuer la répartition appropriée en interne sur ses arguments.

Par exemple, utiliser une classe (qui pourrait implémenter IFoo, mais ne doit pas):

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

Ce qui est intéressant ici, c'est que le anyformulaire est masqué par les remplacements plus spécifiquement saisis.

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
la source
1

Qu'est-ce que la surcharge de fonctions en général?

La surcharge de fonctions ou la surcharge de méthodes est la possibilité de créer plusieurs fonctions du même nom avec différentes implémentations ( Wikipedia )


Qu'est-ce que la surcharge de fonctions dans JS?

Cette fonctionnalité n'est pas possible dans JS - la dernière fonction définie est prise en cas de déclarations multiples:

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

... et en TS?

Les surcharges sont une construction à la compilation sans impact sur le runtime JS:

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

Une erreur d'implémentation en double est déclenchée si vous utilisez le code ci-dessus (plus sûr que JS). TS choisit la première surcharge de montage dans l'ordre descendant, de sorte que les surcharges sont triées du plus spécifique au plus large.


Surcharge de méthode dans TS: un exemple plus complexe

Les types de méthode de classe surchargés peuvent être utilisés de manière similaire pour surcharger les fonctions:

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

Les surcharges très différentes sont possibles, car l'implémentation de la fonction est compatible avec toutes les signatures de surcharge - imposées par le compilateur.

Plus d'infos:

ford04
la source
0

Pour en savoir plus sur les autres, j'ai observé qu'au moins tel que manifesté par TypeScript compilé par WebPack pour Angular 2, vous obtenez tranquillement overWRITTEN au lieu de méthodes overLOADED.

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

Appel:

myComponent.method()

semble exécuter la méthode avec des arguments, ignorant silencieusement la version sans argument, avec une sortie:

with arg
mtyson
la source
2
Vous ne pouvez pas déclarer des corps séparés pour vos surcharges, seulement des signatures différentes.
adharris
5
Je ne sais pas quelle version du compilateur TypeScript vous utilisez, mais la version actuelle émet un Duplicate function implementationavertissement pour le code comme celui-ci.
Royston Shufflebotham du
0

Surcharge de fonction en tapuscrit:

Selon Wikipedia, (et de nombreux livres de programmation), la définition de la surcharge de méthode / fonction est la suivante:

Dans certains langages de programmation, la surcharge de fonctions ou la surcharge de méthodes est la possibilité de créer plusieurs fonctions du même nom avec différentes implémentations . Les appels à une fonction surchargée exécuteront une implémentation spécifique de cette fonction appropriée au contexte de l'appel, permettant à un appel de fonction d'effectuer différentes tâches selon le contexte.

En tapuscrit, nous ne pouvons pas avoir différentes implémentations de la même fonction qui sont appelées en fonction du nombre et du type d'arguments. En effet, lorsque TS est compilé en JS, les fonctions de JS ont les caractéristiques suivantes:

  • Les définitions de fonction JavaScript ne spécifient pas les types de données pour leurs paramètres
  • Les fonctions JavaScript ne vérifient pas le nombre d'arguments lorsqu'elles sont appelées

Par conséquent, au sens strict, on pourrait affirmer que la surcharge de la fonction TS n'existe pas. Cependant, il y a des choses que vous pouvez faire dans votre code TS qui peuvent parfaitement imiter la surcharge de fonctions.

Voici un exemple:

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

Les documents TS appellent cette surcharge de méthode, et ce que nous avons essentiellement fait est de fournir plusieurs signatures de méthode (descriptions des paramètres et types possibles) au compilateur TS. Maintenant, TS peut déterminer si nous avons appelé notre fonction correctement pendant la compilation et nous donner une erreur si nous avons appelé la fonction de manière incorrecte.

Willem van der Veen
la source