Ограничения шаблона подписи индекса машинописного текста

У меня есть несколько интерфейсов Typescript, которые имеют много повторяющихся одинаковых полей. Например:

interface Foo {
   person1Name: string;
   person1Address: string;
   person2Name: string;
   person2Address: string;
   category: string;
   department: string;
}

Я пытаюсь сократить это, используя шаблоны подписей индексов Typescript, которые отлично работают с такими вещами, как [key: 'person${number}Name']. Однако я хотел бы ограничить количество ключей разрешения, скажем, 5 свойствами «человека», и это не работает должным образом.

type AllowedIndex = 1 | 2 | 3 | 4 | 5;

interface Foo {
   [key: `person${AllowedIndex}Name`]: string;
   [key: `person${AllowedIndex}Address`]: string;
   category: string;
   department: string;
}

Вышеупомянутое дает мне ошибку An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.ts(1337).

Есть ли способ сделать то, что я пытаюсь сделать? Или я застрял, повторяя клавиши так, как уже делаю?

Вот Ссылка на TS Playground с тем, что я пробовал.

Если у вас есть несколько полей на номер (имя, адрес и т. д.), рассмотрите возможность рефакторинга во вложенный объект (вероятно, затем используя кортеж из пяти таких объектов). Пожалуйста, помогите себе в будущем и позвольте grep person1 src -rn продолжать работать — не злоупотребляйте такими типами без крайней необходимости, если цифры здесь имеют какое-то реальное семантическое значение.

STerliakov 01.04.2024 21:15

@STerliakov Спасибо за предложение. К сожалению, у меня очень мало контроля над формой объекта, возвращаемого из CMS. Значение цифр в основном является показателем количества разрешенных элементов для данного контента, запрошенного у CMS.

Jmh2013 01.04.2024 21:20

А как насчет этого тогда?

STerliakov 01.04.2024 21:25

Я бы, наверное, написала, что вы делаете вот так. Соответствует ли это вашим потребностям? Если да, то я мог бы написать ответ (хотя он кажется похожим на ответ @STerliakov, так что, возможно, они захотят его написать первым). Если нет, то что мне не хватает?

jcalz 01.04.2024 21:36

Это оба умные решения! Единственное, чего может не хватать (и, возможно, я не смог это прояснить в своем вопросе), так это того, что все пронумерованные поля должны быть необязательными.

Jmh2013 01.04.2024 21:38

Используйте Partial, затем поставьте лайк этому? Если @STerliakov не захочет писать ответ, я это сделаю, если этого будет достаточно. Имеет ли это?

jcalz 01.04.2024 21:40

Да! Спасибо! Напишите ответ, я приму его

Jmh2013 01.04.2024 21:42

@jcalz продолжай, мы это уже обсуждали - я все еще ленивый отвечаю;)

STerliakov 01.04.2024 21:43

(особенно учитывая, что ваше extends решение должно привести к немного более быстрой компиляции, если я правильно помню)

STerliakov 01.04.2024 21:44
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
9
175
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете добиться этого с помощью чего-то вроде этого:

type AllowedIndex = 1 | 2 | 3 | 4 | 5;

type PersonStuff = 
   { [TKey in `person${AllowedIndex}Name`]: string } &
   { [TKey in `person${AllowedIndex}Address`]: string };

interface Foo extends PersonStuff {
   category: string;
   department: string;
}

См. ссылку TS Playground .

Спасибо за подсказку. Теперь я последовал передовому опыту Мэтта Покока — добавлять ко всем дженерикам префикс «T» + имя.

Viktor Luft 01.04.2024 21:45
Ответ принят как подходящий

В TypeScript 4.4 появилась возможность использовать шаблонные или заполнители литеральные типы шаблонов (как реализовано в microsoft/TypeScript#40598 ) в индексных сигнатурах . Но `person${AllowedIndex}Name` не является буквальным типом шаблона шаблона. На самом деле, это вообще даже не шаблонный литеральный тип, в том смысле, что TypeScript охотно оценивает его как объединение строковых литеральных типов "person1Name" | "person2Name" | "person3Name" | "person4Name" | "person5Name". И вы не можете использовать объединение типов строковых литералов в сигнатуре индекса.

Вместо этого вы можете использовать отображаемые типы для создания типа объекта, ключи которого представляют собой объединение строковых литералов. Это будет выглядеть так: {[K in `person${AllowedIndex}Name`]: string}, если вы хотите, чтобы ключи были обязательными, или {[K in `person${AllowedIndex}Name`]?: string}, если вы хотите, чтобы они были необязательными. Аналогичным образом вы можете использовать комбинацию типов утилит Частичная и Запись, например Partial<Record<`person${AllowedIndex}Name`, string>>.

Обратите внимание, что сопоставленные типы являются отдельными типами, и к ним нельзя добавлять свойства. Если вы хотите объединить несколько сопоставленных типов, вы можете использовать пересечение . Или, если вы хотите получить интерфейс , вы можете использовать расширение интерфейса для объединения родительских сопоставленных типов (но только через именованный тип, например Partial или Record) и добавленных вами свойств. Это дает

type AllowedIndex = 1 | 2 | 3 | 4 | 5;

interface Foo extends
   Partial<Record<`person${AllowedIndex}Name`, string>>,
   Partial<Record<`person${AllowedIndex}Address`, string>> {
   category: string;
   department: string;
}

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

type ExpandedFoo = {[K in keyof Foo]: Foo[K]};
/* type ExpandedFoo = {
    category: string;
    department: string;
    person1Name?: string | undefined;
    person2Name?: string | undefined;
    person3Name?: string | undefined;
    person4Name?: string | undefined;
    person5Name?: string | undefined;
    person1Address?: string | undefined;
    person2Address?: string | undefined;
    person3Address?: string | undefined;
    person4Address?: string | undefined;
    person5Address?: string | undefined;
} */

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

Это было очень полезно. Я ценю, что вы добавили дополнительную информацию и ссылки на документацию. Note that mapped types are their own types and you cannot add properties to them — это утверждение зажгло в моем мозгу момент «лампочки» и объясняет, почему кое-что из того, что я пробовал, не работало. Спасибо!

Jmh2013 01.04.2024 23:04

Сейчас я нахожусь в немного другой части своего проекта, в которой есть поля того же типа. Я пытаюсь сгруппировать некоторые из этих пронумерованных полей в цикле, поэтому я нацеливаюсь на эти свойства, используя литерал шаблона. Знаете ли вы, как разрешить этот тип без чрезмерного количества псевдонимов? Вот ссылка на Игровая площадка, чтобы лучше показать то, о чем я спрашиваю.

Jmh2013 02.04.2024 00:23

Это не минимально воспроизводимый пример , и комментарий — не лучшее место для того, чтобы задать вопрос. У вас есть... массив? И вы индексируете его с помощью... строковых ключей? Не понял, извини. При чем тут вообще «Alt»? Вы уверены, что эта проблема не исчезнет, ​​если вы исправите опечатки? В любом случае, это не подходящее место для такого обсуждения. Если вы не можете понять это, возможно, вы захотите открыть для этого новый вопрос. Удачи!

jcalz 02.04.2024 00:33

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