У меня есть несколько интерфейсов 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 с тем, что я пробовал.
@STerliakov Спасибо за предложение. К сожалению, у меня очень мало контроля над формой объекта, возвращаемого из CMS. Значение цифр в основном является показателем количества разрешенных элементов для данного контента, запрошенного у CMS.
А как насчет этого тогда?
Я бы, наверное, написала, что вы делаете вот так. Соответствует ли это вашим потребностям? Если да, то я мог бы написать ответ (хотя он кажется похожим на ответ @STerliakov, так что, возможно, они захотят его написать первым). Если нет, то что мне не хватает?
Это оба умные решения! Единственное, чего может не хватать (и, возможно, я не смог это прояснить в своем вопросе), так это того, что все пронумерованные поля должны быть необязательными.
Используйте Partial, затем поставьте лайк этому? Если @STerliakov не захочет писать ответ, я это сделаю, если этого будет достаточно. Имеет ли это?
Да! Спасибо! Напишите ответ, я приму его
@jcalz продолжай, мы это уже обсуждали - я все еще ленивый отвечаю;)
(особенно учитывая, что ваше extends решение должно привести к немного более быстрой компиляции, если я правильно помню)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете добиться этого с помощью чего-то вроде этого:
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» + имя.
В 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 — это утверждение зажгло в моем мозгу момент «лампочки» и объясняет, почему кое-что из того, что я пробовал, не работало. Спасибо!
Сейчас я нахожусь в немного другой части своего проекта, в которой есть поля того же типа. Я пытаюсь сгруппировать некоторые из этих пронумерованных полей в цикле, поэтому я нацеливаюсь на эти свойства, используя литерал шаблона. Знаете ли вы, как разрешить этот тип без чрезмерного количества псевдонимов? Вот ссылка на Игровая площадка, чтобы лучше показать то, о чем я спрашиваю.
Это не минимально воспроизводимый пример , и комментарий — не лучшее место для того, чтобы задать вопрос. У вас есть... массив? И вы индексируете его с помощью... строковых ключей? Не понял, извини. При чем тут вообще «Alt»? Вы уверены, что эта проблема не исчезнет, если вы исправите опечатки? В любом случае, это не подходящее место для такого обсуждения. Если вы не можете понять это, возможно, вы захотите открыть для этого новый вопрос. Удачи!
Если у вас есть несколько полей на номер (имя, адрес и т. д.), рассмотрите возможность рефакторинга во вложенный объект (вероятно, затем используя кортеж из пяти таких объектов). Пожалуйста, помогите себе в будущем и позвольте
grep person1 src -rnпродолжать работать — не злоупотребляйте такими типами без крайней необходимости, если цифры здесь имеют какое-то реальное семантическое значение.