Sobrecarga de função TypeScript

244

A Seção 6.3 da especificação da linguagem TypeScript fala sobre sobrecarga de funções e fornece exemplos concretos de como implementar isso. No entanto, se eu tentar algo assim:

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

}

Eu recebo um erro do compilador indicando o identificador duplicado, embora os parâmetros de função sejam de tipos diferentes. Mesmo se eu adicionar um parâmetro adicional à segunda função createFeatureLayer, ainda recebo um erro do compilador. Idéias, por favor.

Klaus Nji
fonte
Possível duplicata de sobrecarga
BuZZ-dEE

Respostas:

189

Isso pode ocorrer porque, quando as duas funções são compiladas para JavaScript, sua assinatura é totalmente idêntica. Como o JavaScript não possui tipos, acabamos criando duas funções com o mesmo número de argumentos. Portanto, o TypeScript nos impede de criar essas funções.

O TypeScript suporta sobrecarga com base no número de parâmetros, mas as etapas a serem seguidas são um pouco diferentes se compararmos às linguagens OO. Em resposta a outra pergunta do SO, alguém explicou com um bom exemplo: Sobrecarga de método? .

Basicamente, o que estamos fazendo é criar apenas uma função e várias declarações para que o TypeScript não dê erros de compilação. Quando esse código é compilado para JavaScript, a função concreta sozinha fica visível. Como uma função JavaScript pode ser chamada passando vários argumentos, ela simplesmente funciona.

S. Ravi Kiran
fonte
50
O idioma pode ser alterado para suportar isso. Em teoria, pode-se gerar implementações de funções nomeadas à parte e chamadas pelo TypeScript compilado (por exemplo, createFeatureLayer_1 e createFeatureLayer_2) e createFeatureLayer, em seguida, pode determinar qual delas chamar com base no conteúdo dos argumentos para a interoperação com JavaScript baunilha.
Thomas S. Trias
8
Você escreve como se a sobrecarga no TypeScript só fosse possível com base no número de parâmetros, enquanto a sobrecarga com base no tipo também fosse possível, como mostrado na resposta de Steve Fenton.
Matthijs Wessels
9
Isso é meio coxo; O TypeScript realmente deve estar gerando a "função meta" que escolhe a implementação com nome exclusivo de forma apropriada, com base no que foi passado. Como está agora, há uma falha na qual você pode passar o compilador, mas sua implementação do tipo sniffing pode estar incorreta.
Ez Victor
5
O @EzekielVictor TypeScript faria isso se houvesse uma maneira confiável de verificar os tipos em tempo de execução.
Thorn
3
Isso é ainda mais complicado, é possível com os tipos de JavaScript, mas noções específicas de TS como interfaces, types, enumerações, genéricos etc. são perdidas em tempo de execução. É também por isso que você não pode fazer someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling
209

Quando você sobrecarrega no TypeScript, você tem apenas uma implementação com várias assinaturas.

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

Somente as três sobrecargas são reconhecidas pelo TypeScript como possíveis assinaturas para uma chamada de método, não a implementação real.

No seu caso, eu pessoalmente usaria dois métodos com nomes diferentes, pois não há elementos comuns suficientes nos parâmetros, o que torna provável que o corpo do método precise ter muitos "ifs" para decidir o que fazer.

TypeScript 1.4

A partir do TypeScript 1.4, você normalmente pode remover a necessidade de sobrecarga usando um tipo de união. O exemplo acima pode ser melhor expresso usando:

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

O tipo de aé "um stringou number".

Fenton
fonte
Ótima resposta. Gostaria apenas de destacar que isso pode não ser útil quando se está tentando sobrecarregar por razões como: Gostaria de ter uma instância em que, usando o mesmo construtor, posso passar um objeto definindo todas as propriedades esperadas e em a única instância, passar parâmetros individuais: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
Em geral, eu prefiro usar um método de fábrica para criar-me um objeto em cada sentido - não há necessidade de ramo se você chamar Foo.fromObject(obj)e Foo.fromJson(str)e assim por diante.
Fenton
Mas isso postula que alguém sempre passará seus parâmetros como um objeto ou uma única sequência, e se eu quiser que eles sejam passados ​​separadamente, como destacado no meu comentário anterior? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Notei também que você tem métodos diferentes na Fooclasse fromObject e fromJson?
Hlawuleka MAS
1
Se você seguir essa diferença de volta à fonte, geralmente descobrirá que não há necessidade dela. Por exemplo, você precisa digitar myNumou de myObjqualquer maneira, por que não ter métodos separados e deixar tudo claro / evitar lógica de ramificação desnecessária.
Fenton
2
Observe que o uso de um tipo de união pode ser problemático se você quiser ter diferentes tipos de retorno com base nos parâmetros. Isso pode ser resolvido com genéricos se o tipo de retorno sempre corresponder a um dos tipos de parâmetro, mas para outros casos, sobrecargas são a melhor solução.
John Montgomery
45

Você pode declarar uma função sobrecarregada declarando a função como tendo um tipo que possui várias assinaturas de chamada:

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

Então o seguinte:

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

A definição real da função deve ser singular e executar o despacho apropriado internamente em seus argumentos.

Por exemplo, usando uma classe (que pode implementar IFoo, mas não precisa):

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

O interessante aqui é que o anyformulário está oculto pelas substituições digitadas mais especificamente.

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
fonte
1

O que é sobrecarga de função em geral?

Sobrecarga de função ou sobrecarga de método é a capacidade de criar várias funções com o mesmo nome com diferentes implementações ( Wikipedia )


O que é sobrecarga de função em JS?

Esse recurso não é possível no JS - a última função definida é utilizada no caso de várias declarações:

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

... e em TS?

Sobrecargas são uma construção em tempo de compilação sem impacto no tempo de execução 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

Um erro de implementação duplicado é acionado, se você usar o código acima (mais seguro que o JS). O TS escolhe a primeira sobrecarga de montagem na ordem descendente, para que as sobrecargas sejam classificadas da mais específica à mais ampla.


Sobrecarga de método no TS: um exemplo mais complexo

Os tipos de métodos de classe sobrecarregados podem ser usados ​​de maneira semelhante à sobrecarga de funções:

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

As sobrecargas muito diferentes são possíveis, pois a implementação da função é compatível com todas as assinaturas de sobrecarga - impostas pelo compilador.

Mais informações:

ford04
fonte
0

Como aviso aos outros, observei que, pelo menos como manifestado pelo TypeScript compilado pelo WebPack para Angular 2, você silenciosamente supera os métodos WRITTEN em vez de overLOADED.

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

A ligar:

myComponent.method()

parece executar o método com argumentos, ignorando silenciosamente a versão sem argumento, com saída:

with arg
mtyson
fonte
2
Você não pode declarar corpos separados para suas sobrecargas, apenas assinaturas diferentes.
adharris
5
Não tenho certeza de qual versão do compilador TypeScript você está usando, mas a versão atual emite um Duplicate function implementationaviso para código como este.
Royston Shufflebotham
0

Sobrecarga de função em texto datilografado:

Segundo a Wikipedia, (e muitos livros de programação), a definição de sobrecarga de método / função é a seguinte:

Em algumas linguagens de programação, sobrecarga de função ou sobrecarga de método é a capacidade de criar várias funções com o mesmo nome com implementações diferentes . As chamadas para uma função sobrecarregada executarão uma implementação específica dessa função apropriada ao contexto da chamada, permitindo que uma chamada de função execute tarefas diferentes, dependendo do contexto.

No texto datilografado, não podemos ter implementações diferentes da mesma função que são chamadas de acordo com o número e o tipo de argumentos. Isso ocorre porque quando o TS é compilado para JS, as funções em JS têm as seguintes características:

  • As definições de função JavaScript não especificam tipos de dados para seus parâmetros
  • As funções JavaScript não verificam o número de argumentos quando chamados

Portanto, em sentido estrito, pode-se argumentar que a sobrecarga da função TS não existe. No entanto, existem coisas que você pode fazer no seu código TS que podem imitar perfeitamente a sobrecarga de funções.

Aqui está um exemplo:

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

Os documentos do TS chamam esse método de sobrecarga, e o que basicamente fizemos foi fornecer várias assinaturas de métodos (descrições de possíveis parâmetros e tipos) ao compilador TS. Agora, o TS pode descobrir se chamamos nossa função corretamente durante o tempo de compilação e nos dar um erro se chamarmos a função incorretamente.

Willem van der Veen
fonte