Допустим, у меня есть тип 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', который представляет собой просто строковые литералы.
Возможно ли это вообще?
Или вы спрашиваете, как создавать типы строковых литералов (которые выглядят как шаблоны), например этот?
@jcalz Извините, я не понял вашего последнего предложения, So is the use case narrow like number or wide like arbitrary template literals?. Я могу сказать вам, только показав тип ввода и желаемый тип вывода, который я описал в тексте. Но да, это number конкретно! Все литералы шаблона от `...${number}...` до '...${number}...'.
@jsejcksn Да, я хочу вот так вот так. Я использовал жесткое кодирование только для того, чтобы показать, какой результат я хочу.
@jcalz Ваш пример - это то, что я искал! Не могли бы вы опубликовать это как свой ответ, чтобы другие люди могли легко найти ваше решение?
Я опубликую ответ, когда у меня будет возможность.






Примечание. Меня интересует только поддержка замены шаблона `${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"
Выглядит неплохо.
Специально для
number? Это возможно, вот так. Для всех литералов шаблонов, таких как от`${T}`до"${T}"? нееет, ничего так не работает. Это все равно, что попросить TypeScript превратить переменную с именемabcв строку"abc". Итак, является ли вариант использования узким, какnumber, или широким, как произвольные литералы шаблона?