Подстроки строковых объединений с использованием литеральных типов шаблонов

У меня есть объединение нескольких разных строк, представляющих все ключи перевода, поддерживаемые приложением. Они записываются в стандартизированной форме с '.' в качестве разделителей. Например:

type Keys = 'foo.bar' | 'baz.bar' | 'fail.error';

Я хотел бы получить из этого союза другой союз, содержащий все корневые строки, оканчивающиеся на .bar. Итак, для этого примера желаемый результат равен 'foo' | 'baz'

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

type Namespace<T extends string = string> = `${T}.bar` extends Keys ? T : never;

const example0: Namespace<'foo'> = 'foo'; // Allowed, but i had to explicitly give the type
const example1: Namespace<'notFound'> = 'notFound'; // Correctly gives an error

Я хотел бы сделать следующее, но моя текущая версия приводит к ошибке в каждой строке:

const example2: Namespace = 'foo'; // Should be allowed, but isn't
const example3: Namespace = 'baz'; // Should be allowed, but isn't
const example4: Namespace = 'fail'; // Should be an error
const example5: Namespace = 'notFound'; // Should be an error

Есть ли лучший способ сделать этот тип?

Ссылка на игровую площадку

Соответствует ли этот подход вашим потребностям? Если да, то я могу написать ответ; если нет, то что мне не хватает? Обратите внимание, что принято писать типы в UpperPascalCase, поэтому я заменил keys на Keys.

jcalz 09.04.2022 21:12

Да, похоже, это сработает. Я экспериментировал с infer, но не смог заставить его работать. Если ты напишешь, я приму

Nicholas Tower 09.04.2022 21:13

Другим общим вариантом может быть Детская площадка.

Daniel Rodríguez Meza 09.04.2022 21:18
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
2
3
25
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Мой подход здесь заключался бы в создании тип объекта дистрибутива (как придумано в Майкрософт/TypeScript#47109), где мы сначала создаем отображаемый тип поверх Keys, где каждый ключ P имеет свойство, которое является первой частью P, если оно заканчивается на ".bar", или never в противном случае. И затем мы немедленно индексировать в этого типа объекта с Keys, чтобы получить тип Namespaceсоюз, который мы хотим. Это служит для распределения операции извлечения части перед ".bar" по объединению записей в Keys:

type Namespace = { [P in Keys]: P extends `${infer K}.bar` ? K : never }[Keys];
// type Namespace = "foo" | "baz"

При необходимости это можно обобщить на Prefix<T, U>, который принимает объединение строковых литералов T и суффикс U и возвращает префикс элементов T, за которыми следует суффикс U:

type Prefix<T extends string, U extends string> =
    { [P in T]: P extends `${infer K}${U}` ? K : never }[T];

type AlsoNamespace = Prefix<Keys, ".bar">
// type AlsoNamespace = "foo" | "baz"

Ссылка на код для игровой площадки

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