Подписи перегрузки и подпись реализации в машинописном тексте

avatar
Sean Liu
1 июля 2021 в 16:38
361
2
2

Я читаю руководство по Typescript, и мне трудно понять, почему в следующем фрагменте кода появляется сообщение об ошибке:

function fn(x: string): void;
function fn(vo) {
  // ...
}
// Expected to be able to call with zero arguments
fn();

Вот объяснение, но я не мог его понять. Может ли кто-нибудь объяснить мне, что здесь происходит?

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

Источник
kaya3
1 июля 2021 в 16:42
1

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

jcalz
1 июля 2021 в 16:49
0

Что такое vo и почему у него нет аннотации типа? Почему вы используете перегрузки здесь? Без реализации трудно понять, каково ваше намерение и почему вы удивлены появлением сообщения об ошибке. Код function fn(x: string) {}, за которым следует fn(), будет иметь сообщение об ошибке вообще без каких-либо перегрузок... функция fn() ожидает вызова с аргументом string, а вы его не указали.

Ответы (2)

avatar
David Nichero
14 апреля 2022 в 18:32
0

ВВЕДЕНИЕ - ПРОЧИТАТЬ

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

ОТВЕТ ХАОМИНА

Для меня ответ @Haomin действительно не имеет смысла. Например:

Объявление метода перегрузки работает только для метода с 2 и 2+ аргументами...

Документы говорят следующее:

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

Подписи НЕ ЯВЛЯЮТСЯ аргументами. Аргументы — это значения, которые передаются функции при ее вызове. Подпись функции отличается (имя, параметры, тип и т.д. функции) -> Подпись функции - MDN Docs.

Вы увидите, что его пример работает, если вы сделаете это следующим образом:

function fn3(): void;
function fn3(x: string): void;
function fn3(x?: string): void {
    if (x === undefined) {
        console.log("branch 1")
    } else {
       console.log(`branch 2 -> ${x}`)
    }
}

fn3()
fn3("x")

Все, что я сделал, это добавил еще одну подпись, которая принимала параметр (x). Почему? Потому что, как говорится в документации, подпись реализации не видна извне. Подпись реализации является последней подписью, в данном случае:

.
function fn3(x?: string): void)

Итак, чтобы обернуть все это: Убедитесь, что ВСЕ необходимые подписи написаны над подписью реализации, имейте в виду, что подпись реализации не видна снаружи и что она должна содержать все другие возможности подписи.

ПОЧЕМУ ПОДПИСЬ РЕАЛИЗАЦИИ НЕ ПОКАЗАНА СНАРУЖИ

(подумал, что может быть и другие причины). В качестве примера возьмем следующий код:

function fn3(x: string): void;
function fn3(x: string, y: number, z: number): void;
function fn3(x: string, y?: number, z?: number): void {
    if (y === undefined) {
        console.log(`X is -> ${x}`)
    } else {
        console.log(`X is -> ${x}`)
        console.log(`Y is -> ${y}`)
        console.log(`Z is -> ${z}`)
    }
}

fn3("x")
fn3("x", 2, 3)

Представьте, что он принял сигнатуру реализации. Что произошло бы, если бы мы вызвали fn3 как fn3("x", 2) без третьего параметра, поскольку он необязателен? Это бы разбилось или дало бы неопределенность. Возможно, именно поэтому TS не "показывает" сигнатуру реализации снаружи, потому что такие вещи могли бы произойти. Дэвид

avatar
Haomin
10 июля 2021 в 02:30
3

Меня это тоже очень смутило. Исходя из фона 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. перегрузка 1 -> function fn(x: string): void;
  2. перегрузка 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>.