TypeScript надає функціональність overloading (перевантаження) функцій, тобто можливість однієї функції:
- приймати змінну кількість аргументів
- приймати аргументи різних типів
- повертати різні типи
Сигнатура функції
Розглянемо приклад функції, яка повертає повідомлення використовуючи ім’я людини:
function message(person: string): string {
return `Hello, ${person}.`;
}Наведена вище функція, приймає один аргумент типу string. Викликається ця функція наступним чином:
console.log(message('Jhon')); // Hello, Jhon.Уявімо, що є потреба зробити функцію message() більш гнучкою, щоб вона додатково могла приймати список осіб, яких слід привітати. Така функція повинна приймати в якості аргументу рядок або масив рядків, а також повертати рядок або масив рядків.
Для визначення такої функції є два підходи. Перший підхід передбачає модифікацію сигнатури функції, шляхом оновлення типу параметрів та типу що повертається:
function message(person: string | string[]): string | string[] {
if (typeof person === 'string') {
return `Hello, ${person}.`;
} else if (Array.isArray(person)) {
return person.map((name) => `Hello, ${name}.`);
}
throw new Error('Unable to message');
}Тепер функцію message() можна викликати двома способами:
console.log(message('Jhon')); // Hello, Jhon.
console.log(message(['Rory', 'Rodney'])); // [ 'Hello, Rory.', 'Hello, Rodney.' ]Overloading (перевантаження) функції
Другим підходом є використання перевантаження функції, котре полягає у визначенні декількох сигнатур перевантаження (Overload Signatures), та однієї сигнатури реалізації (Implementation Signature):
- Сигнатура перевантаження визначає кількість та типи параметрів, а також тип що повертається функцією, і не має тіла. Функція може мати декілька сигнатур перевантаження, відповідно до різних способів її виклику.
- Сигнатура реалізації має більш загальні типи параметрів та тип що повертається, але також має тіло, яке містить реалізацію роботи функції. При цьому сигнатура реалізації може бути тільки одна.
Оновимо функцію message() з використанням overloading:
// Overload signatures
function message(person: string): string;
function message(persons: string[]): string[];
// Implementation signature
function message(person: unknown): unknown {
if (typeof person === 'string') {
return `Hello, ${person}.`;
} else if (Array.isArray(person)) {
return person.map((name) => `Hello, ${name}.`);
}
throw new Error('Unable to message');
}Тепер функція message() має дві сигнатури перевантаження та одну сигнатуру реалізації. Кожна сигнатура перевантаження описує один спосіб виклику функції (в цьому прикладі функцію message() можна викликати двома способами: з рядком або масивом рядків в якості аргументу). Сигнатура реалізації містить логіку роботи функції.
Приклади виклику функції message():
console.log(message('Jhon')); // Hello, Jhon.
console.log(message(['Rory', 'Rodney'])); // [ 'Hello, Rory.', 'Hello, Rodney.' ]Слід додати, що хоча сигнатура реалізації містить поведінку функції, її не можна викликати безпосередньо. Викликаються лише сигнатури перевантаження:
const value: unknown = 'Unknown';
message(value);
// Error: No overload matches this call.Сигнатура реалізації
Типи у сигнатурі реалізації повинні бути достатньо загальними, щоб забезпечити її сумісність з сигнатурами перевантаження.
Розглянемо приклад:
// Overload signatures
function message(person: string): string;
function message(persons: string[]): string[];
// Implementation signature
function message(person: unknown): string {
// ...
throw new Error('Unable to message');
}У цьому прикладі, тип повернення сигнатури реалізації – string, недостатньо загальний, щоб бути сумісним з типом повернення string[], однієї з сигнатур перевантаження.
Перевантаження кількості параметрів
Приклад перевантаження функції, що може приймати змінну кількість аргументів:
// Overload signatures
function message(): string;
function message(name: string): string;
function message(firstName: string, lastName: string): string;
// Implementation signature
function message(firstName?: string, lastName?: string): string {
if (firstName && lastName) {
return `Hello, ${firstName} ${lastName}.`;
} else if (firstName) {
return `Hello, ${firstName}.`;
} else {
return `Hello.`;
}
}
console.log(message()); // Hello.
console.log(message('Alice')); // Hello, Alice.
console.log(message('John', 'Doe')); // Hello, John Doe.У цьому прикладі, в сигнатурі реалізації використовуються необов’язкові параметри (?), для обробки різних варіантів виклику функції message().
Перевантаження методів
Вище розглянуто приклади перевантаження функцій, застосовані до звичайної функції. Також TypeScript надає можливість робити overloading методів. Під час перевантаження методу, сигнатури перевантаження і сигнатура реалізації є частиною класу.
Для прикладу створимо клас Message, з перевантаженим методом greet():
class Message {
text: string;
constructor(text: string) {
this.text = text;
}
// Overload signatures
greet(person: string): string;
greet(persons: string[]): string[];
// Implementation signature
greet(person: unknown): unknown {
if (typeof person === 'string') {
return `${this.text}, ${person}.`;
} else if (Array.isArray(person)) {
return person.map((name) => `${this.text}, ${name}.`);
}
throw new Error('Unable to message');
}
}
const message = new Message('Hello');
console.log(message.greet('Jhon')); // Hello, Jhon.
console.log(message.greet(['Olivia', 'Jim'])); // [ 'Hello, Olivia.', 'Hello, Jim.' ]