Перевантаження функцією TypeScript


243

Розділ 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. Відповідаючи на ще одне питання ТА, хтось пояснив це гарним прикладом: Метод перевантаження? .

В основному, ми робимо лише одну функцію та декілька декларацій, щоб TypeScript не давав помилок компіляції. Коли цей код буде скомпільований у JavaScript, буде видно лише конкретна функція. Оскільки функцію JavaScript можна викликати шляхом передачі декількох аргументів, вона просто працює.


50
Мова може бути змінена для підтвердження цього. Теоретично можна створити реалізацію функцій, які названі окремо і викликані компільованим TypeScript (наприклад, createFeatureLayer_1 та createFeatureLayer_2), а createFeatureLayer потім міг би визначити, яку слід викликати на основі вмісту аргументів для взаємодії з ванільним JavaScript.
Томас С. Тріас

8
Ви пишете це так, як якщо б перевантаження в TypeScript можлива лише на основі кількості параметрів, тоді як перегрузка на основі типу також можлива, як показано у відповіді Стіва Фентона.
Matthijs Wessels

9
Це свого роду кульга; TypeScript справді повинен генерувати "мета-функцію", яка вибирає однозначно названу реалізацію належним чином на основі того, що було передано. Як це зараз, є розрив, куди ви можете передати компілятор, але ваша реалізація типу нюху може бути неправильною.
Єзекіїль Віктор

5
@EzekielVictor TypeScript зробив би це, якби надійний спосіб перевірити типи під час виконання.
thorn̈

3
Це ще складніше, це можливо для типів JavaScript, але специфічні для TS поняття, такі як інтерфейси, types, enums, generics тощо, втрачаються під час виконання. Тому також не можна робити someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling

209

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

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

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

У вашому випадку я особисто використовував би два способи з різними іменами, оскільки недостатньо спільності в параметрах, що робить, ймовірно, органу методу потрібно мати багато "ifs", щоб вирішити, що робити.

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) {} }
парами

Загалом, я б вважав за краще використовувати фабричний метод , щоб створити мені об'єкт , кожен шлях - немає необхідності в галузь , якщо ви телефонуєте Foo.fromObject(obj)і Foo.fromJson(str)і так далі.
Фентон

Але це постулати, що кожен завжди передаватиме їх параметри як об'єкт, так і як окремий рядок, що робити, якщо я хочу, щоб вони проходили окремо, як підкреслено в попередньому коментарі? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Я також помітив, що у вас є різні методи у Fooкласі, відObject та fromJson?
Хлавулека МАС

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 вибирає перше примірне перевантаження в порядку зверху вниз, тому перевантаження сортуються від найбільш специфічних до найбільш широких.


Метод перевантаження в ТС: більш складний приклад

Типи методів перевантаженого класу можна використовувати аналогічно для функціонування перевантаження:

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

Можливі дуже різні перевантаження, оскільки реалізація функції сумісна з усіма підписами перевантаження - накладає компілятор.

Інфо:


0

Наголошуючи на інших, я зауважив, що принаймні, як це проявляється в TypeScript, складеному WebPack для Angular 2, ви спокійно отримуєте overWRITTEN замість перевантажених методів.

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

Дзвінки:

myComponent.method()

здається, що виконується метод аргументами, мовчки ігноруючи версію no-arg, з виходом:

with arg

2
Ви не можете оголошувати окремі органи для своїх перевантажень, лише різних підписів.
адхарріс

5
Я не впевнений, яку версію компілятора TypeScript ви використовуєте, але поточна версія надсилає Duplicate function implementationпопередження для такого коду.
Royston Shufflebotham

0

Перевантаження функцій у машинописі:

Згідно з Вікіпедією, (і багатьма книгами програмування) визначення методу / функції перевантаження таке:

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

У typecript ми не можемо мати різних реалізацій однієї і тієї ж функції, які викликаються відповідно до кількості та типу аргументів. Це тому, що коли TS компілюється в JS, функції в JS мають такі характеристики:

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

Тому, в строгому сенсі, можна стверджувати, що перевантаження функцій 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 може зрозуміти, чи правильно ми викликали нашу функцію під час компіляції, і помилитися, якщо ми неправильно викликали функцію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.