Sovraccarico della funzione TypeScript


244

La sezione 6.3 delle specifiche del linguaggio TypeScript parla del sovraccarico delle funzioni e fornisce esempi concreti su come implementarlo. Tuttavia, se provo qualcosa del genere:

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

}

Ottengo un errore del compilatore che indica un identificatore duplicato anche se i parametri della funzione sono di tipi diversi. Anche se aggiungo un parametro aggiuntivo alla seconda funzione createFeatureLayer, visualizzo ancora un errore del compilatore. Idee, per favore.


Possibile duplicato del sovraccarico
BuZZ-dEE,

Risposte:


189

Ciò può essere dovuto al fatto che, quando entrambe le funzioni sono compilate in JavaScript, la loro firma è totalmente identica. Dato che JavaScript non ha tipi, finiamo per creare due funzioni prendendo lo stesso numero di argomenti. Pertanto, TypeScript ci impedisce di creare tali funzioni.

TypeScript supporta il sovraccarico in base al numero di parametri, ma i passaggi da seguire sono leggermente diversi se confrontati con i linguaggi OO. In risposta a un'altra domanda SO, qualcuno l'ha spiegato con un bell'esempio: sovraccarico del metodo? .

Fondamentalmente, quello che stiamo facendo è che stiamo creando solo una funzione e un numero di dichiarazioni in modo che TypeScript non dia errori di compilazione. Quando questo codice viene compilato in JavaScript, la sola funzione concreta sarà visibile. Poiché una funzione JavaScript può essere richiamata passando più argomenti, funziona.


50
La lingua potrebbe essere modificata per supportare questo. In teoria, si potrebbero generare implementazioni di funzioni che sono distinte e chiamate da TypeScript compilato (ad esempio createFeatureLayer_1 e createFeatureLayer_2) e createFeatureLayer potrebbe quindi determinare quale chiamare chiamare in base al contenuto degli argomenti per l'interoperazione con JavaScript vaniglia.
Thomas S. Trias,

8
Dici come se il sovraccarico in TypeScript sia possibile solo in base al numero di parametri, mentre il sovraccarico in base al tipo è anche possibile, come mostrato nella risposta di Steve Fenton.
Matthijs Wessels

9
Questo è un po 'zoppo; TypeScript dovrebbe davvero generare la "meta funzione" che sceglie l'implementazione con un nome univoco in modo appropriato in base a ciò che è stato passato. Com'è ora c'è una spaccatura in cui potresti passare il compilatore ma la tua implementazione del tipo sniffing potrebbe essere errata.
Ezechiele Vittorio,

5
@EzekielVictor TypeScript lo farebbe se esistesse un modo affidabile per controllare i tipi in fase di esecuzione.
thorn̈

3
Questo è ancora più complicato, è fattibile con i tipi di JavaScript, ma le nozioni specifiche di TS come interfacce, types, enum, generici, ecc., Vengono perse in fase di esecuzione. Questo è anche il motivo per cui non puoi farlo someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling,

209

Quando sovraccarichi in TypeScript, hai solo un'implementazione con più firme.

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

Solo i tre sovraccarichi sono riconosciuti da TypeScript come possibili firme per una chiamata di metodo, non dall'implementazione effettiva.

Nel tuo caso, utilizzerei personalmente due metodi con nomi diversi in quanto non c'è abbastanza comunanza nei parametri, il che rende probabile che il corpo del metodo dovrà avere un sacco di "if" per decidere cosa fare.

TypeScript 1.4

A partire da TypeScript 1.4, in genere è possibile rimuovere la necessità di un sovraccarico utilizzando un tipo di unione. L'esempio sopra può essere espresso meglio usando:

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

Il tipo di aè "o stringo number".


Bella risposta. Vorrei solo evidenziarlo, questo potrebbe non essere utile quando si cerca di sovraccaricare per motivi come: vorrei avere un'istanza, dove usando lo stesso costruttore, posso passare un oggetto che definisce tutte le proprietà previste e in l'una istanza, passa singoli parametri: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS

In generale, io preferisco usare un metodo factory per creare me un oggetto ogni modo - non c'è bisogno di ramo se si chiama Foo.fromObject(obj)e Foo.fromJson(str)e così via.
Fenton,

Ma ciò postula che uno passerà sempre i propri parametri come oggetto o come singola stringa, e se volessi farli passare separatamente, come evidenziato dal mio commento precedente? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Ho anche notato che hai diversi metodi nella Fooclasse, fromObject e fromJson?
Hlawuleka MAS

1
Se segui questa differenza alla fonte, di solito scoprirai che non ce n'è bisogno. Ad esempio, devi digitare myNumo myObjcomunque, quindi perché non avere metodi separati e rendere tutto chiaro / evitare logiche di ramificazione non necessarie.
Fenton,

2
Si noti che l'utilizzo di un tipo di unione può essere problematico se si desidera avere tipi di restituzione diversi in base ai parametri. Ciò può essere risolto con i generici se il tipo restituito corrisponde sempre a uno dei tipi di parametro, ma per gli altri casi i sovraccarichi sono la soluzione migliore.
John Montgomery,

45

È possibile dichiarare una funzione sovraccarica dichiarando che la funzione ha un tipo con più firme di invocazione:

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

Quindi quanto segue:

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 definizione effettiva della funzione deve essere singolare ed eseguire il dispacciamento appropriato internamente sui suoi argomenti.

Ad esempio, usando una classe (che potrebbe implementare IFoo, ma non è necessario):

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

La cosa interessante qui è che il anymodulo è nascosto dalle sostituzioni più specificate.

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

1

Qual è il sovraccarico delle funzioni in generale?

Il sovraccarico di funzioni o il sovraccarico di metodi è la capacità di creare più funzioni con lo stesso nome con implementazioni diverse ( Wikipedia )


Che cos'è il sovraccarico delle funzioni in JS?

Questa funzione non è possibile in JS: l'ultima funzione definita viene utilizzata in caso di più dichiarazioni:

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

... e in TS?

I sovraccarichi sono un costrutto in fase di compilazione senza alcun impatto sul 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

Se si utilizza il codice sopra (più sicuro di JS) viene generato un errore di implementazione duplicato. TS sceglie il primo sovraccarico del raccordo in ordine dall'alto verso il basso, quindi i sovraccarichi vengono ordinati dal più specifico al più ampio.


Sovraccarico di metodo in TS: un esempio più complesso

I tipi di metodi di classe sovraccaricati possono essere utilizzati in modo simile per il sovraccarico delle funzioni:

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

Sono possibili sovraccarichi molto diversi, poiché l'implementazione della funzione è compatibile con tutte le firme di sovraccarico - applicata dal compilatore.

Altre informazioni:


0

Come testa a testa per gli altri, ho osservato che almeno come manifestato da TypeScript compilato da WebPack per Angular 2, si ottiene tranquillamente il metodo SCRITTO anziché il metodo SCARICATO.

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

Calling:

myComponent.method()

sembra eseguire il metodo con argomenti, ignorando silenziosamente la versione no-arg, con output:

with arg

2
Non puoi dichiarare corpi separati per i tuoi sovraccarichi, solo firme diverse.
adharris,

5
Non sono sicuro della versione del compilatore TypeScript che stai utilizzando, ma la versione corrente emette un Duplicate function implementationavviso per codice come questo.
Royston Shufflebotham,

0

Sovraccarico della funzione in dattiloscritto:

Secondo Wikipedia, (e molti libri di programmazione) la definizione di metodo / funzione di sovraccarico è la seguente:

In alcuni linguaggi di programmazione, il sovraccarico delle funzioni o il sovraccarico dei metodi è la possibilità di creare più funzioni con lo stesso nome con implementazioni diverse . Le chiamate a una funzione sovraccarica eseguiranno un'implementazione specifica di quella funzione appropriata al contesto della chiamata, consentendo a una chiamata di funzione di eseguire diverse attività a seconda del contesto.

In dattiloscritto non possiamo avere implementazioni diverse della stessa funzione chiamate in base al numero e al tipo di argomenti. Questo perché quando TS viene compilato in JS, le funzioni in JS hanno le seguenti caratteristiche:

  • Le definizioni delle funzioni JavaScript non specificano i tipi di dati per i loro parametri
  • Le funzioni JavaScript non controllano il numero di argomenti quando vengono chiamate

Pertanto, in senso stretto, si potrebbe sostenere che il sovraccarico della funzione TS non esiste. Tuttavia, ci sono cose che puoi fare nel tuo codice TS che possono imitare perfettamente il sovraccarico della funzione.

Ecco un esempio:

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

I documenti TS chiamano questo metodo un sovraccarico, e quello che sostanzialmente abbiamo fatto è fornire più firme di metodo (descrizioni di possibili parametri e tipi) al compilatore TS. Ora TS può capire se abbiamo chiamato la nostra funzione correttamente durante il tempo di compilazione e darci un errore se abbiamo chiamato la funzione in modo errato.

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.