Параметр типа подписки индекса не может быть объединенным типом. Рассмотрите возможность использования типизированного объекта вместо этого!
Я пытаюсь использовать следующий паттерн:
enum Option {
ONE = 'one',
TWO = 'two',
THREE = 'three'
}
interface OptionRequirement {
someBool: boolean;
someString: string;
}
interface OptionRequirements {
[key: Option]: OptionRequirement;
}
На первый взгляд, всё выглядит довольно понятно, однако я получаю следующую ошибку:
An index signature parameter type cannot be a union type. Consider using a mapped object type instead.
Что я делаю не так?
5 ответ(ов)
Самое простое решение — использовать Record:
type OptionRequirements = Record<Options, OptionRequirement>;
Вы также можете реализовать это самостоятельно следующим образом:
type OptionRequirements = {
[key in Options]: OptionRequirement;
}
Этот конструкция доступна только для type, но не для interface.
Проблема в вашем определении заключается в том, что вы указываете, что ключ вашего интерфейса должен быть типа Options, в то время как Options является перечислением, а не строкой, числом или символом.
key in Options означает "для тех специфических ключей, которые находятся в объединенном типе Options".
Псевдоним type более гибок и мощен по сравнению с interface.
Если ваш тип не нужно использовать в классе, выбирайте type вместо interface.
В вашем случае проблема заключалась в использовании двоеточия : в определении типа для ключей в объекте. Это вызывало ошибку. Чтобы исправить это, вы можете использовать оператор in, который позволяет корректно задать типы для ключей.
Вот исправленный код:
export type PossibleKeysType =
| 'userAgreement'
| 'privacy'
| 'people';
interface ProviderProps {
children: React.ReactNode;
items: {
// ↙ используйте оператор "in"
[key in PossibleKeysType]: Array<SectionItemsType>;
};
}
Таким образом, если вы замените : на in, ваш интерфейс будет корректно определять ключи, и это решит возникшую проблему.
У меня была похожая проблема, но в моем случае это касалось другого свойства интерфейса. В качестве примера, вот мое решение с необязательным свойством поля и перечислением для ключей:
export enum ACTION_INSTANCE_KEY {
cat = 'cat',
dog = 'dog',
cow = 'cow',
book = 'book'
}
type ActionInstances = {
[key in ACTION_INSTANCE_KEY]?: number; // id кота/id собаки/id коровы и т.д. // <== необязательное
};
export interface EventAnalyticsAction extends ActionInstances { // <== нужно расширять
marker: EVENT_ANALYTIC_ACTION_TYPE; // <== если хотите добавить еще одно поле в интерфейс
}
Таким образом, вы можете добавить любые дополнительные поля в интерфейс EventAnalyticsAction, сохраняя при этом возможность использования перечисления для ключей, что значительно упрощает работу с ними. Не забудьте, что свойства, указанные в ActionInstances, являются необязательными, поэтому вы можете работать с частичными данными.
В вашем случае, если вам нужно, чтобы свойства были опциональными, вы можете создать следующий обобщенный тип:
type PartialRecord<K extends string | number | symbol, T> = { [P in K]?: T; };
После этого вы можете использовать его следующим образом:
type MyTypes = 'TYPE_A' | 'TYPE_B' | 'TYPE_C';
interface IContent {
name: string;
age: number;
}
interface IExample {
type: string;
partials: PartialRecord<MyTypes, IContent>;
}
Вот пример использования:
const example: IExample = {
type: 'some-type',
partials: {
TYPE_A: {
name: 'name',
age: 30
},
TYPE_C: {
name: 'another name',
age: 50
}
}
}
Такой подход позволяет вам гибко задавать опциональные свойства в объекте partials, при этом обеспечивая хорошую типизацию и поддержку IntelliSense.
Вместо использования интерфейса, можно применить тип отображаемого объекта. Вот пример, как это можно сделать:
enum Option {
ONE = 'one',
TWO = 'two',
THREE = 'three'
}
type OptionKeys = keyof typeof Option;
interface OptionRequirement {
someBool: boolean;
someString: string;
}
type OptionRequirements = { // обратите внимание, это тип, а не интерфейс
[key in OptionKeys]: OptionRequirement; // здесь используется синтаксис key in
}
В этом примере мы создаем перечисление Option для определения возможных значений. Затем мы используем keyof для получения ключей этого перечисления, чтобы создать тип OptionKeys.
С помощью синтаксиса отображаемого типа мы объявляем OptionRequirements, который будет объектом, где каждый ключ соответствует значению из Option, а значением является объект типа OptionRequirement. Это позволяет избежать использования интерфейса и делает код более декларативным и читабельным.
Возможно ли расширять типы в TypeScript?
Когда использовать JSX.Element, ReactNode и ReactElement?
Переопределение типа свойства интерфейса, определённого в файле d.ts TypeScript
Как проверить, содержит ли массив строку в TypeScript?
ESLint: 8.0.0 Не удалось загрузить плагин '@typescript-eslint'