Перегрузка функции TypeScript

244

Раздел 6.3 спецификации языка TypeScript говорит о перегрузке функций и дает конкретные примеры того, как это реализовать. Однако, если я попробую что-то вроде этого:

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

}

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

Клаус Нджи
источник
Возможный дубликат перегрузки метода?
BuZZ-DEE

Ответы:

189

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

TypeScript поддерживает перегрузку в зависимости от количества параметров, но шаги, которые необходимо выполнить, немного отличаются, если сравнивать с языками OO. В ответ на другой вопрос SO кто-то объяснил это хорошим примером: перегрузка метода? ,

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

С. Рави Киран
источник
50
Язык может быть изменен, чтобы поддержать это. Теоретически, можно генерировать реализации функций, которые именуются отдельно и вызываются скомпилированным TypeScript (например, createFeatureLayer_1 и createFeatureLayer_2), а затем createFeatureLayer может определить, какую из них вызывать, основываясь на содержимом аргументов для взаимодействия с ванильным JavaScript.
Томас С. Триас
8
Вы произносите это так, как будто перегрузка в TypeScript возможна только в зависимости от количества параметров, а перегрузка в зависимости от типа также возможна, как показано в ответе Стива Фентона.
Маттис Вессельс,
9
Это немного отстойно; TypeScript действительно должен генерировать «мета-функцию», которая выбирает реализацию с уникальным именем, исходя из того, что было передано. Как это сейчас, есть трещина, в которой вы можете передать компилятор, но ваша реализация сниффинга типов может быть неправильной.
Иезекииль Виктор
5
@EzekielVictor TypeScript сделает это, если существует надежный способ проверки типов во время выполнения.
Торн
3
Это еще сложнее, это выполнимо с типами JavaScript, но специфичные для TS понятия, такие как интерфейсы type, перечисления, обобщения, и т. Д., Теряются во время выполнения. Вот почему ты не можешь сделать someObject instanceof ISomeInterfaceDefinedInTypeScript.
Морган Тувери Квиллинг
209

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

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

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

В вашем случае я бы лично использовал два метода с разными именами, так как в параметрах недостаточно общего, что делает возможным, чтобы тело метода нуждалось в большом количестве «если», чтобы решить, что делать.

TypeScript 1.4

Начиная с TypeScript 1.4, вы обычно можете устранить необходимость перегрузки, используя тип объединения. Приведенный выше пример можно лучше выразить, используя:

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

Тип a«или stringили number».

Фентон
источник
Отличный ответ. Я просто хотел бы подчеркнуть, что это может быть бесполезно, когда кто-то пытается перегрузить по таким причинам, как: я хотел бы иметь экземпляр, где с помощью того же конструктора я могу передать объект, определяющий все ожидаемые свойства, и в один случай, передать отдельные параметры: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
В общем, я бы предпочел использовать фабричный метод , чтобы создать мне объект , каждый путь - нет необходимости в отрасль , если вы звоните Foo.fromObject(obj)и Foo.fromJson(str)и так далее.
Фентон
Но это постулирует, что каждый будет всегда передавать свои параметры как объект или как одну строку, что, если я хочу, чтобы они передавались отдельно, как выделено в моем предыдущем комментарии? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Я также заметил, что у вас есть разные методы в Fooклассе, отObject и от Json?
Хлавулека MAS
1
Если вы проследите это различие до источника, вы, как правило, обнаружите, что в этом нет необходимости. Например, вы должны ввести myNumили в myObjлюбом случае, так почему бы не иметь отдельные методы и сделать все ясно / избежать ненужной логики ветвления.
Фентон
2
Обратите внимание, что использование типа объединения может быть проблематичным, если вы хотите иметь разные типы возврата в зависимости от параметров. Эту проблему можно решить с помощью обобщений, если тип возвращаемого значения всегда соответствует одному из типов параметров, но в других случаях перегрузки являются лучшим решением.
Джон Монтгомери
45

Вы можете объявить перегруженную функцию, объявив функцию как имеющую тип с несколькими сигнатурами вызова:

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

Тогда следующее:

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

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

Например, используя класс (который может реализовать IFoo, но не обязан):

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

Здесь интересно то, что anyформа скрыта за более конкретно напечатанными переопределениями.

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

Что такое перегрузка функций в целом?

Функция перегрузки или перегрузки методов является возможность создания нескольких функций на одном имени с различными реализациями ( Википедия )


Что такое перегрузка функций в JS?

Эта функция невозможна в JS - последняя определенная функция берется в случае нескольких объявлений:

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

... а в ТС?

Перегрузки являются конструкцией времени компиляции, не влияющей на время выполнения 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

Двойная ошибка реализации возникает, если вы используете приведенный выше код (более безопасный, чем JS). TS выбирает первую фитинговую перегрузку в порядке сверху вниз, поэтому перегрузки сортируются от наиболее специфических к наиболее широким.


Перегрузка метода в TS: более сложный пример

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

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

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

Больше информации:

ford04
источник
0

Как замечание к другим, я заметил, что, по крайней мере, как показывает TypeScript, скомпилированный WebPack для Angular 2, вы спокойно получаете overWRITTEN вместо overLOADED методов.

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

Вызов:

myComponent.method()

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

with arg
mtyson
источник
2
Вы не можете объявлять отдельные тела для ваших перегрузок, только разные подписи.
adharris
5
Я не уверен, какую версию компилятора TypeScript вы используете, но текущая версия выдает Duplicate function implementationпредупреждение для такого кода.
Ройстон
0

Перегрузка функций в машинописи:

Согласно Википедии (и многим книгам по программированию) определение перегрузки метода / функции следующее:

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

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

  • В определениях функций JavaScript не указываются типы данных для их параметров
  • Функции JavaScript не проверяют количество аргументов при вызове

Следовательно, в строгом смысле можно утверждать, что перегрузка функции TS не существует. Однако есть некоторые вещи, которые вы можете сделать в своем коде TS, которые могут идеально имитировать перегрузку функций.

Вот пример:

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

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

Виллем ван дер Веен
источник