Есть ли способ выполнить перегрузку метода в TypeScript?

109

Есть ли способ выполнить перегрузку метода на языке TypeScript?

Я хочу добиться чего-то вроде этого:

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

Вот пример того, что я не хочу делать (я действительно ненавижу эту часть перегрузочного хака в 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);
Пустота-995
источник
1
Да, шанс есть, язык еще не умер. Есть еще вопросы?
hakre
6
@hakre Это странно, учитывая, что TypeScript уже поддерживает перегрузку методов.
svick 02
@svick: ну, вы называете это перегрузкой метода? В вашем ответе сам метод не перегружен, одно тело здесь.
hakre
2
@hakre В спецификации это называется перегрузкой метода. Вы, конечно, можете возразить, что это не очень хорошая версия, но я думаю, вы не можете сказать, что ее вообще не существует.
svick 02
@svick: Я тоже не сказал. Но мне кажется, что шансы, о которых спрашивает OP, связаны с ментальной моделью перегрузки метода. Для разделения волос мы могли бы сказать, что это перегрузка сигнатуры метода;)
hakre

Ответы:

165

Согласно спецификации, TypeScript поддерживает перегрузку методов, но это довольно неудобно и включает в себя множество ручных проверок типов параметров. Я думаю, это в основном потому, что самое близкое к перегрузке методов в простом JavaScript включает и эту проверку, а TypeScript пытается не изменять фактические тела методов, чтобы избежать ненужных затрат на производительность во время выполнения.

Если я правильно понимаю, вы должны сначала написать объявление метода для каждой из перегрузок, а затем одну реализацию метода, которая проверяет свои аргументы, чтобы решить, какая перегрузка была вызвана. Подпись реализации должна быть совместима со всеми перегрузками.

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 вообще не поддерживает перегрузку методов, верно? Так как же возврат к JS поможет?
svick
А как насчет проверки интерфейсов? Есть ли у вас лучшее решение, чем это: stackoverflow.com/questions/14425568/… ?
DiPix
хм .. мне это не очень нравится, лучше просто иметь необязательный параметр.
LuckyLikey
3
Я думаю, что здесь важно то, что ваш код будет читаться без тонны проверки типов операторов if. Какая разница, во что это превращается.
Brain2000
34

Обновите для ясности. Перегрузка методов в TypeScript - полезная функция, поскольку она позволяет создавать определения типов для существующих библиотек с API, который необходимо представить.

Однако при написании собственного кода вы вполне можете избежать когнитивных накладных расходов, связанных с перегрузками, используя необязательные параметры или параметры по умолчанию. Это более удобочитаемая альтернатива перегрузкам методов, а также обеспечивает честность вашего API, поскольку вы избегаете создания перегрузок с неинтуитивным упорядочением.

Общий закон перегрузок TypeScript:

Если вы можете удалить сигнатуры перегрузки и все ваши тесты пройдут, вам не нужны перегрузки TypeScript.

Обычно вы можете добиться того же с помощью необязательных параметров или параметров по умолчанию - или с типами объединения, или с небольшой объектной ориентацией.

Актуальный вопрос

Фактический вопрос требует перегрузки:

someMethod(stringParameter: string): void {

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

Теперь даже в языках, которые поддерживают перегрузки с отдельными реализациями (примечание: перегрузки TypeScript используют одну реализацию) - программисты дают советы по обеспечению согласованности в порядке. Это сделало бы подписи:

someMethod(stringParameter: string): void {

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

stringParameterВсегда требуется, поэтому он идет первым. Вы можете написать это как рабочую перегрузку TypeScript:

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

Но следуя закону перегрузок TypeScript, мы можем удалить сигнатуры перегрузки, и все наши тесты все равно пройдут.

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

Актуальный вопрос в актуальном порядке

Если бы вы были настроены сохранить исходный порядок, перегрузки были бы:

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

Теперь нужно много разветвлений, чтобы решить, куда поместить параметры, но вы действительно хотели сохранить этот порядок, если читаете так далеко ... но подождите, что произойдет, если мы применим закон перегрузок 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;
    }
  }
}

Уже достаточно разветвлений

Конечно, учитывая количество необходимых проверок типов ... возможно, лучший ответ - просто иметь два метода:

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

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}
Фентон
источник
это обычно не называется перегрузкой метода. также см. вопрос, меняется тип первого параметра.
hakre
3
Я признал это в своем ответе - вы должны указать необязательный параметр последним, поэтому numberпараметр будет вторым аргументом и будет необязательным. TypeScript не поддерживает «правильные» перегрузки методов - но даже мир C # переходит от перегрузок к необязательным параметрам, поскольку во многих случаях это приводит к более читаемому коду.
Fenton
Вы имеете в виду if (typeof numberParameter != 'undefined'), верно;)
Хуан Мендес
Это зависит от вашего варианта использования, в частности от того, принимаете ли вы нули. Если вам нужно это сделать, не забудьте использовать, !==чтобы избежать жонглирования.
Fenton
1
@Sebastian, который звучит как возможность для внедрения зависимостей. Учитывая, что перегрузка метода в TypeScript включает в себя один метод с несколькими декорациями, параметрами по умолчанию и типами объединения, обеспечивает лучший опыт. Если методы различаются более существенно, вы либо используете абстракцию, либо реализуете несколько методов.
Fenton
7

Я желаю. Мне тоже нужна эта функция, но TypeScript должен быть совместим с нетипизированным JavaScript, который не имеет перегруженных методов. т.е. если ваш перегруженный метод вызывается из JavaScript, он может быть передан только одной реализации метода.

Есть несколько соответствующих дискуссий по codeplex. например

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

Я все еще думаю, что TypeScript должен генерировать все if'ы и переключения, чтобы нам не пришлось это делать.

никополептик
источник
2

Почему бы не использовать в качестве аргумента функции интерфейс, определяемый дополнительным свойством .

Для случая в этом вопросе использование встроенного интерфейса, определенного с некоторыми дополнительными свойствами, может напрямую создавать код, подобный приведенному ниже:

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

Поскольку перегрузка, предоставляемая в TypeScript, является, как упоминалось в других комментариях, просто списком различных сигнатур функций без поддержки соответствующих кодов реализации, таких как другие статические языки. Таким образом, реализация по-прежнему должна выполняться только в одном теле функции, что делает использование перегрузки функций в Typescript не таким комфортным, как такие языки, поддерживающие реальную функцию перегрузки.

Тем не менее, все еще есть много новых и удобных вещей, предоставляемых в машинописном тексте, которые недоступны в устаревшем языке программирования, где дополнительная поддержка свойств в анонимном интерфейсе является таким подходом, чтобы соответствовать удобной зоне от перегрузки устаревших функций, я думаю.

千 木 郷
источник
0

Если существует много вариантов перегрузки методов, другой способ - просто создать класс со всеми вашими аргументами внутри. Таким образом, вы можете передать только нужный параметр в любом порядке.

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

Также вы можете создать конструктор со значениями по умолчанию и / или передать некоторые обязательные аргументы. Тогда просто используйте вот так:

const config = new SomeMethodConfig('text');
config.numberParameter = 123; // initialize an optional parameter only if you want to do it
this.SomeMethod(config);
Игорь
источник
-2

Javascript не имеет понятия о перегрузке. Typescript - это не C # или Java.

Но вы можете реализовать перегрузку в Typescript.

Прочтите этот пост http://www.gyanparkash.in/function-overloading-in-typescript/

Gyan
источник
К сожалению, ссылка не работает. Вы можете сделать репост?
LHM,
@LMH: Вот почему ответы только по ссылкам не приветствуются.
Дэн Даскалеску,