Что означает `...` в выражении типа, выведенном TypeScript?

У меня есть функция say, которая работает следующим образом:

  • say() ==> ""
  • say("hello")() ==> "hello"
  • say("hello")("how")("are")("you")() ==> "hello how are you"

и реализуется в TypeScript следующим образом:

function say(first?: string) {
  if (first === undefined) {
    return ""
  }
  return (second?: string) => {
    if (second === undefined) {
      return first
    }
    return say(`${first} ${second}`)
  }
}

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

function say(first?: string): "" | ((second?: string) => string | ...)

Знак ..., похоже, указывает на тот факт, что любая попытка вывести тип является циклической, поскольку

say возвращает либо пустую строку, либо функцию, которая возвращает строку, либо функцию, которая возвращает строку, либо функцию, которая возвращает строку или...

Однако использование ... при рендеринге типа в TypeScript не проясняет, где находится цикличность или рекурсия, и тип кажется неточным. Возможно, это выбор команды TypeScript. Есть ли способ более точно выразить тип? Что-то в духе

функция, которая при задании необязательного строкового параметра возвращает либо пустую строку, либо тип t, причем t — это тип функции, которая при задании необязательного строкового параметра возвращает либо строку, либо t

Выглядит как рекурсивный тип type F = (first?: string) => (string | F);. Однако я не уверен, что язык позволит вам вызывать его более одного раза, поскольку одним из возможных типов возврата является просто string, который не может быть вызван.

Wiktor Zychla 13.08.2024 09:14

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

jsejcksn 13.08.2024 10:55
^ Предполагая, что вы просто ищете точную информацию о типе сигнатуры вашей функции (в частности, тип возвращаемого значения), вот пример, который демонстрирует рекурсивный перегруженный возвращаемый тип (и слегка измененную реализацию). Соответствует ли это вашим потребностям? Если да, то я могу написать это в качестве ответа. Если нет, то что я пропустил?
jsejcksn 13.08.2024 11:07

Ответ здесь заключается в том, что, хотя TS имеет внутреннее представление анонимных рекурсивных типов, он не может точно сериализовать их как строку. По адресу ms/TS#44045 есть запрос на функцию, позволяющую TS синтезировать имена типов, но это вряд ли произойдет. Вы можете синтезировать имя самостоятельно, как показано по ссылке на эту игровую площадку. Это полностью решает вопрос? Если да, то я напишу ответ; если нет, то чего не хватает?

jcalz 13.08.2024 14:54

@jcalz Очень понятно, значит, тип действительно внутренне представим, но TS предпочитает сериализовать его с помощью ..., чтобы указать цикличность, а не выбирать связанную переменную, чтобы точно показать, где происходит рекурсия? Ссылка на игровую площадку чрезвычайно полезна и, безусловно, достойна ответа, если вы захотите ее написать. Псевдоним типа очень понятен!

Ray Toal 13.08.2024 16:23

@jsejcksn Хороший отзыв по вопросу, отредактированный для пояснения основного вопроса, который не зависит от TypeScript. Ваша детская площадка очень полезна! По какой-то причине я забыл, что выражения type сами по себе могут быть самореферентными, и зациклился на их выражении с помощью какой-то связанной переменной. Мне нужно отложить учебники по теории и написать больше TypeScript!

Ray Toal 13.08.2024 16:31
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
6
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это связано с тем, что механизм вывода типов TS имеет ограничения при сериализации и отображении сложных рекурсивных типов. Хотя TS может обрабатывать эти рекурсивные типы, иногда у него возникают проблемы с их полным отображением, поэтому может показаться, что тип является неполным или циклическим.

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

type Say = (s?: string) => s extends undefined ? string : Say

это рекурсивный тип, и если функция получает строку, т.е. s не является undefined, она возвращает другую функцию типа Say и ff s не определена, она возвращает строку

Здесь вы можете найти интересные дискуссии по этому поводу:

Рекурсивные типы в TypeScript

https://github.com/microsoft/TypeScript/issues/3496

Это ответ на ваш вопрос?

«TS не может обрабатывать эти сложные рекурсивные типы непосредственно в своей системе типов». Тип прекрасно обрабатывается в системе типов. В настоящее время его просто невозможно сериализовать в строку для отображения. Возможно вы это имели в виду, но это кажется двусмысленным. Не могли бы вы отредактировать, чтобы уточнить? Существуют циклические типы, с которыми TS действительно не может «обработать» в своей системе типов (например, что-то вроде type X = "" | `${X}a`), но в этом вопросе происходит не это.

jcalz 13.08.2024 14:43

Вы правы — это может привести к путанице. Я обновлю свой ответ, чтобы прояснить этот момент. Спасибо, что указали на это!

Syed Mustaqhim 13.08.2024 16:51

Другие вопросы по теме