У меня есть объединение нескольких разных строк, представляющих все ключи перевода, поддерживаемые приложением. Они записываются в стандартизированной форме с '.'
в качестве разделителей. Например:
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
Есть ли лучший способ сделать этот тип?
Да, похоже, это сработает. Я экспериментировал с infer
, но не смог заставить его работать. Если ты напишешь, я приму
Другим общим вариантом может быть Детская площадка.
Мой подход здесь заключался бы в создании тип объекта дистрибутива (как придумано в Майкрософт/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"
Соответствует ли этот подход вашим потребностям? Если да, то я могу написать ответ; если нет, то что мне не хватает? Обратите внимание, что принято писать типы в UpperPascalCase, поэтому я заменил
keys
наKeys
.