Existe uma maneira de sobrecarregar o método no TypeScript?


109

Existe uma maneira de sobrecarregar o método na linguagem TypeScript?

Eu quero alcançar algo assim:

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

Aqui está um exemplo do que eu não quero fazer (eu realmente odeio essa parte do hack de sobrecarga em 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
Sim, há uma chance, a linguagem ainda não morreu. Mais alguma pergunta?
hakre,

6
@hakre É uma coisa estranha de se dizer, considerando que o TypeScript já oferece suporte à sobrecarga de método.
svick

@svick: bem, você chama esse método de sobrecarga? Em sua resposta, o método em si não está sobrecarregado, um corpo se movendo ao redor.
hakre,

2
@hakre A especificação chama isso de sobrecarga de método. Certamente, você pode argumentar que não é uma versão particularmente boa disso, mas acho que não pode dizer que não exista.
svick

@svick: Eu também não disse. Mas parece-me que as chances que OP pergunta são específicas sobre o modelo mental de sobrecarga de método. Para a divisão do cabelo, poderíamos dizer que é uma sobrecarga de assinatura de método;)
hakre

Respostas:


165

De acordo com a especificação, o TypeScript oferece suporte à sobrecarga de método, mas é bastante estranho e inclui muitos tipos de parâmetros de verificação de trabalho manual. Acho que é principalmente porque o mais próximo que você pode chegar da sobrecarga de método em JavaScript simples inclui essa verificação também e o TypeScript tenta não modificar os corpos de método reais para evitar qualquer custo de desempenho de tempo de execução desnecessário.

Se bem entendi, você deve primeiro escrever uma declaração de método para cada uma das sobrecargas e, em seguida, uma implementação de método que verifica seus argumentos para decidir qual sobrecarga foi chamada. A assinatura da implementação deve ser compatível com todas as sobrecargas.

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 não oferece suporte a sobrecarga de método, certo? Então, como voltar para JS vai ajudar?
svick

Que tal verificar interfaces? Você tem uma solução melhor do que essa: stackoverflow.com/questions/14425568/… ?
DiPix

hmm .. eu realmente não gosto disso, apenas ter um parâmetro opcional é melhor ler.
LuckyLikey

3
Acho que o importante aqui é manter seu código legível sem uma tonelada de instruções if de verificação de tipo. Quem se importa com o que isso transpila.
Brain2000 de

34

Atualize para maior clareza. A sobrecarga de método no TypeScript é um recurso útil, na medida em que permite criar definições de tipo para bibliotecas existentes com uma API que precisa ser representada.

Ao escrever seu próprio código, entretanto, você pode evitar a sobrecarga cognitiva de sobrecargas usando parâmetros opcionais ou padrão. Esta é a alternativa mais legível para sobrecargas de método e também mantém sua API honesta, pois você evitará a criação de sobrecargas com pedidos não intuitivos.

A lei geral de sobrecargas de TypeScript é:

Se você pode excluir as assinaturas de sobrecarga e todos os seus testes passarem, você não precisa de sobrecargas de TypeScript

Normalmente, você pode conseguir o mesmo com parâmetros opcionais ou padrão - ou com tipos de união, ou com um pouco de orientação a objetos.

A questão real

A questão real pede uma sobrecarga de:

someMethod(stringParameter: string): void {

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

Agora, mesmo em linguagens que suportam sobrecargas com implementações separadas (nota: sobrecargas de TypeScript compartilham uma única implementação) - os programadores são aconselhados a fornecer consistência nos pedidos. Isso faria com que as assinaturas:

someMethod(stringParameter: string): void {

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

O stringParameteré sempre necessário, por isso é o primeiro. Você pode escrever isso como uma sobrecarga de TypeScript funcional:

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

Mas, seguindo a lei de sobrecargas do TypeScript, podemos excluir as assinaturas de sobrecarga e todos os nossos testes ainda passarão.

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

A questão real, na ordem real

Se você estivesse determinado a persistir com o pedido original, as sobrecargas seriam:

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

Agora, há muitas ramificações para descobrir onde colocar os parâmetros, mas você realmente queria preservar essa ordem se estiver lendo até aqui ... mas espere, o que acontece se aplicarmos a lei de sobrecargas do 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;
    }
  }
}

Já chega de ramificação

Claro, dada a quantidade de verificação de tipo que precisamos fazer ... talvez a melhor resposta seja simplesmente ter dois métodos:

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

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

isso geralmente não é conhecido como sobrecarga de método. veja também a pergunta, o tipo das primeiras mudanças de parâmetro.
hakre,

3
Eu reconheci que em minha resposta - você colocaria o parâmetro opcional por último, então o numberparâmetro seria o segundo argumento e seria opcional. O TypeScript não oferece suporte a sobrecargas de método "adequadas" - mas mesmo o mundo C # está se afastando das sobrecargas em direção a parâmetros opcionais, pois em muitos casos isso leva a um código mais legível.
Fenton

Você quer dizer if (typeof numberParameter != 'undefined'), certo;)
Juan Mendes

Isso depende do seu caso de uso, em particular se você aceita zeros. Se precisar fazer isso, lembre-se de usar !==para evitar malabarismos.
Fenton

1
@Sebastian, parece uma oportunidade para injeção de dependência. Dado que a sobrecarga de método no TypeScript envolve um único método com várias decorações, parâmetros padrão e tipos de união, fornece uma experiência melhor. Se os métodos diferirem de maneiras mais substanciais, você usa uma abstração ou implementa vários métodos.
Fenton

7

Eu gostaria. Eu quero esse recurso também, mas o TypeScript precisa ser interoperável com JavaScript não tipado, que não tem métodos sobrecarregados. ou seja, se seu método sobrecarregado for chamado de JavaScript, ele só poderá ser despachado para uma implementação de método.

Existem algumas discussões relevantes no codeplex. por exemplo

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

Ainda acho que o TypeScript deve gerar todos os if'ing e switching para que não precisemos fazer isso.


2

Por que não usar a interface opcional definida pela propriedade como o argumento da função.

Para o caso desta questão, o uso de uma interface inline definida com algumas propriedades opcionais somente poderia fazer o código diretamente como algo abaixo:

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

Porque a sobrecarga fornecida no TypeScript é, conforme mencionado nos comentários de outros, apenas uma lista de diferentes assinaturas de função sem oferecer suporte a códigos de implementação correspondentes como outras linguagens estáticas. Portanto, a implementação ainda precisa ser feita em apenas um corpo de função, o que torna o uso de sobrecarga de função em Typescript não tão confortável quanto essas linguagens suportam o recurso de sobrecarga real.

No entanto, ainda há muitos materiais novos e convenientes fornecidos no texto datilografado que não está disponível na linguagem de programação legada, onde o suporte de propriedade opcional em uma interface anônima é uma abordagem para encontrar a zona confortável da sobrecarga de função legada, eu acho.


0

Se houver muitas variações de sobrecargas de método, outra maneira é apenas criar uma classe com todos os seus args dentro. Assim, você pode passar apenas o parâmetro que deseja em qualquer ordem.

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

Além disso, você pode criar um construtor com valores padrão e / ou passar alguns argumentos obrigatórios. Então use assim:

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.