Меня это тоже очень смутило. Исходя из фона Kotlin/Java Android, я должен сказать, что перегруженные машины Typescript требуют времени, чтобы понять.
Во-первых, нам нужно решить, как объявить overload
в Typescript
Проблема 1: неправильный способ объявления перегрузок [не является причиной вопроса]
Моей первоначальной идеей по поводу обработки перегрузок было написать следующий код:
function makeDate(timestamp: number): void { /* code */ }
function makeDate(m: number, d: number, y: number): void { /* code */ }
Я немедленно столкнулся с ошибкой lint Duplicate function implementation.ts(2393)
. Сначала я был сбит с толку, так как именно так вы объявляете overload
в Java и Kotlin. Однако на самом деле это не так, как вы объявляете overload
в Typescript, и я считаю, что это основная причина проблем.
Ответ на вопрос 1:
Оказывается, в Typescript объявление overload
не связано с наличием двух методов с одинаковыми именами и разными сигнатурами в каждой из их собственных реализаций. Вместо этого сначала определяются методы с одинаковыми именами и разными сигнатурами без тела и, наконец, предоставляется метод с реализацией (тело метода), который имеет возможность обрабатывать все ранее объявленные методы без тела.
Проблема 2: неправильное количество аргументов для требования overload
[причина вопроса]
Теперь, когда я понял, как правильно создавать перегруженные методы в Typescript, я сразу же начал писать следующий код:
[код 2.1]
function fn(x: string): void; // <-- Define one way to call method
function fn(x: string, y: string, z: string): void; // <-- Define another way to call method with more param (overloaded way)
function fn(x: string, y?: string, z?: string): void { // <--- provide a method with a body that can handle both previous two declarations
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y} :: ${z}`)
}
}
fn("x")
fn("xxx", "yyy", "zzz")
Выходы:
$ branch 1
$ branch 2 -> xxx :: yyy :: zzz
В приведенном выше фрагменте кода мы объявили 1 метод с 2 различными перегрузками.
- перегрузка 1 ->
function fn(x: string): void;
- перегрузка 2 ->
function fn(x: string, y: string, z: string): void;
И они оба обрабатываются методом "concrete":
function fn(x: string, y?: string, z?: string): void {
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y} :: ${z}`)
}
}
Поскольку у этого метода есть тело, я называю его конкретным методом. Также обратите внимание, что этот метод обрабатывает случай с @params: x
, а если @params: y and z
является необязательным, то при этом он охватывает вызовы методов {overload1
и overload2
}. И это допустимая перегрузка.
.
Следуя тому же принципу, я пошел дальше и написал следующий код:
[код 2.2]
function fn2(x: string): void;
function fn2(x: string, y: string): void;
function fn2(x: string, y?: string): void {
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y}`)
}
}
fn2("x")
fn2("x", "y")
Выходы:
$ branch 1
$ branch 2 -> x :: y
Код тоже сработал, как я и подозревал. Поэтому я написал еще немного кода:
[код 2.3]
function fn3(): void;
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
fn3()
fn3("x")
Но в этот раз код не сработал и tsc
жалуется:
Playground.ts:219:5 - error TS2554: Expected 0 arguments, but got 1.
219 fn3("x")
~~~
Found 1 error.
Теперь пришло время уделить немного времени пониманию цитаты из документации:
Подпись реализации не видна снаружи. При написании перегруженной функции вы всегда должны иметь две или более подписи над реализацией функции.
Это было очень запутанно, если вы слишком много думали, оказалось, что вы можете просто понять это как "У вас должно быть 2+ аргумента, чтобы выполнять перегрузки", поэтому пример кода [code 2.1]
и [code 2.2]
сработало. С:
- Для
[code 2.1]
у него было 1 и 3 аргументов. Что соответствует требованиям упомянутой документации
- Для
[code 2.2]
было 1 и 2. Что также соответствует требованиям документации.
- Однако для
[code 2.3]
было 0 и 1. Это не соответствует требованию вы всегда должны иметь две или более подписи над реализацией функции в документах, поэтому tsc
жалуется.
И это действительно имеет смысл, поскольку:
function fn3(): void;
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
То же самое, что просто определить один аргумент с необязательным параметром ?
:
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
Итак, ответ на вопрос 2:
Объявление метода перегрузки работает только для метода с 2 и 2+ аргументами, а для обработки всех случаев, объявленных вами для перегрузок, требуется метод с телом. Кроме того, это не работает с методами, имеющими аргументы от 0 до 1, в этом случае tsc
будет жаловаться Expected 0 arguments, but got 1.ts(2554)
. Между тем, объявление перегрузки для методов с аргументами 0 и 1 является избыточным, так как вы можете просто объявить метод с 1 необязательным параметром54<269381>.
Что конкретно в сообщении об ошибке непонятно? В нем говорится, что у вас должно быть две или более подписи над реализацией, а у вас есть только одна. Кроме того, ваша сигнатура перегрузки не позволяет вызывать функцию с нулевыми аргументами, поэтому непонятно, почему вы ожидаете, что сможете это сделать.
Что такое
vo
и почему у него нет аннотации типа? Почему вы используете перегрузки здесь? Без реализации трудно понять, каково ваше намерение и почему вы удивлены появлением сообщения об ошибке. Кодfunction fn(x: string) {}
, за которым следуетfn()
, будет иметь сообщение об ошибке вообще без каких-либо перегрузок... функцияfn()
ожидает вызова с аргументомstring
, а вы его не указали.