Как заменить тип ${number} на строковый тип ${number} в TypeScript?

Допустим, у меня есть тип Foo:

type Foo = 'apple' | `a.${number}` | `a.${number}.c`

В конце концов я хочу преобразовать его в Boo:

type Boo = ReplaceTemplateWithLiteral<Foo>
// 'apple' | 'a.${number}' | 'a.${number}.c'

Я знаю, что могу извлечь тип строки, включающий ${number}, следующим образом:

Extract<Foo, `${string}.${number}` | `${string}.${number}.${string}`>
// `a.${number}` | `a.${number}.c`

Но я не знаю, как преобразовать его в 'a.${number}' | 'a.${number}.c', который представляет собой просто строковые литералы.

Возможно ли это вообще?

Специально для number? Это возможно, вот так. Для всех литералов шаблонов, таких как от `${T}` до "${T}"? нееет, ничего так не работает. Это все равно, что попросить TypeScript превратить переменную с именем abc в строку "abc". Итак, является ли вариант использования узким, как number, или широким, как произвольные литералы шаблона?

jcalz 09.06.2024 17:43

Или вы спрашиваете, как создавать типы строковых литералов (которые выглядят как шаблоны), например этот?

jsejcksn 09.06.2024 17:45

@jcalz Извините, я не понял вашего последнего предложения, So is the use case narrow like number or wide like arbitrary template literals?. Я могу сказать вам, только показав тип ввода и желаемый тип вывода, который я описал в тексте. Но да, это number конкретно! Все литералы шаблона от `...${number}...` до '...${number}...'.

Changdae Park 09.06.2024 17:51

@jsejcksn Да, я хочу вот так вот так. Я использовал жесткое кодирование только для того, чтобы показать, какой результат я хочу.

Changdae Park 09.06.2024 17:55

@jcalz Ваш пример - это то, что я искал! Не могли бы вы опубликовать это как свой ответ, чтобы другие люди могли легко найти ваше решение?

Changdae Park 09.06.2024 18:03

Я опубликую ответ, когда у меня будет возможность.

jcalz 09.06.2024 19:28
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Примечание. Меня интересует только поддержка замены шаблона `${number}`/заполнителя литерального типа шаблона (см. microsoft/TypeScript#40598 ) на строку литерального типа "${number}". Я не собираюсь беспокоиться о замене других литералов шаблона заполнителя, таких как общий один из форм `${T}` (где T — параметр универсального типа) или `${string}`. Если у вас есть один из них в литерале вашего шаблона, тогда все ставки отключены.

Вы можете сделать это следующим образом:

type ReplaceNumberWithLiteral<T extends string, A extends string = ""> =
  T extends `${infer F}${infer R}` ?
  ReplaceNumberWithLiteral<R, `${A}${`${number}` extends F ? "${number}" : F}`> : 
  A;

Здесь мы используем хвостовой рекурсивный условный тип для обхода строкового литерала символ за символом, и если он находит `${number}`, то заменяет его на "${number}", в противном случае он оставляет найденное в покое. Обратите внимание, что обнаружить `${number}` немного сложнее; вы не можете проверить F extends `${number}`, потому что если F является числовым символом, например "3", то это будет успешным, и вы не хотите превращать "ABC123" в "ABC${number}${number}${number}". Я изменил его на `${number}` extends F, который подходит для тех случаев использования, которые мы хотим поддерживать. Конечно, если F есть `${string}`, то это удастся, и поэтому что-то вроде `a${string}b`, вероятно, превратится в "a${number}b". Если это важно, то можно попытаться изменить приведенный выше код, но это выходит за рамки заданного вопроса.

Давайте убедимся, что это работает:

type Foo = 'apple' | `a.${number}` | `a.${number}.c`
type Bar = ReplaceNumberWithLiteral<Foo>
//   ^? type Bar = "apple" | "a.${number}" | "a.${number}.c"

Выглядит неплохо.

Детская площадка, ссылка на код

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