Получение типа подмножества массива в TypeScript

#typescript #webstorm

#машинописный текст #webstorm

Вопрос:

У меня есть структура данных (это может быть массив или объект), которая содержит некоторые данные настроек. В настоящее время это выглядит так:

 const settings = [
  {name: 'Albert', value: 'a', isDefault: true}, 
  {name: 'Bradley', value: 'b'}
] as const
 

и тип

 type TAllValues = settings[number]['value']  // is 'a' | 'b' <-- that Union Type is what I want
 

Что я надеюсь сделать, так это иметь возможность фильтровать этот список, чтобы получить подмножество на основе условия соответствия в настройке. Что-то вроде этого

 type TDefaultValues = FilterSettings<settings, {isDefault: true}> // would be just 'a'
 

где я могу передать type значение, имеющее эту форму, condition и получить тип объединения значений для тех записей, которые соответствуют условию.

Я также мог бы изменить структуру данных на объект, если бы это помогло:

 const settings = {
  a: {name: 'Albert', isDefault: true}, 
  b: {name: 'Bradley'}
}
 

Редактировать
Примечание — Существует проблема с WebStorm, которая по-разному обрабатывает определенные объединения. Я не выяснил, почему это было бы, но Barkers и Barkers2 одинаковы во всех других средах, которые я пробовал. В Webstorm использование расширений для подмножества объекта из «как const» равно false , везде это true .

 const Animals = [
  { animal: 'cat', bark: false },
  { animal: 'dog', bark: true }
] as const

type TAnimals = typeof Animals[number]
type TAnimals2 = { animal: 'cat'; bark: false } | { animal: 'dog'; bark: true }

type Barkers = Extract<TAnimals, { bark: true }>  // <--- never
type Barkers2 = Extract<TAnimals2, { bark: true }>  // <--- correct
 

Начальный тип не должен быть Никогда

Ответ №1:

Вы можете использовать распределительный условный тип, чтобы проверить, соответствует ли настройка условию, и выбрать только те, которые соответствуют:

 const settings = [
  { name: 'Albert', value: 'a', isDefault: true },
  { name: 'Bradley', value: 'b' }
] as const

type Setting = typeof settings[number]

type FilterSettings<Condition, S extends Setting = Setting> = 
  S extends Condition ? S["value"] : never

type TDefaultValues = FilterSettings<{ isDefault: true }> // "a"
 

Игровая площадка

Комментарии:

1. Спасибо, это было большим подспорьем для обработки массива. Но я возвращаюсь never к TDefaultValues. Это работает, если я добавляю тип для SettingItem type SettingItem = { name: string, value: string, isDefault?: boolean } и пересекаюсь с ним type Setting = typeof settings[number] amp; SettingItem

2. Вы можете увидеть, как это работает в прикрепленной игровой площадке. Я предполагаю, что тип settings , с которым вы тестируете, не такой, как в вопросе.

3. Оказывается, в WebStorm все по-другому. Игровая площадка, проверка типов и VS-код одинаковы и работают так, как вы описали.

4. Хм, это странно. Попробуйте проверить, какая версия TS используется WebStorm (возможно, встроенная вместо установленной в node_modules)

5. Я согласен, это странно. Оба являются 4.5.2. Что может делать Webstorm? Я добавил проблему с intellij, и там была пара связанных. Это связано с тем, как создается тип объединения ‘to const’ / readonly по сравнению с литеральным — хотя я также подтвердил, что они оба extends разные. Возможно, наконец-то пришло время навсегда переключиться на VS Code.