Тип параметра подписи индекса не может быть типом объединения. Вместо этого рассмотрите возможность использования сопоставленного типа объекта

102

Я пытаюсь использовать следующий шаблон:

enum Option {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three'
}

interface OptionRequirement {
  someBool: boolean;
  someString: string;
}

interface OptionRequirements {
  [key: Option]: OptionRequirement;
}

Мне это кажется очень простым, однако я получаю следующую ошибку:

Тип параметра подписи индекса не может быть типом объединения. Вместо этого рассмотрите возможность использования сопоставленного типа объекта.

Что я делаю не так?

Джон Маккарти
источник
1
Тип keyможет быть только строкой, числом или символом. перечисление нет.
unional

Ответы:

157

Вы можете использовать оператор TS "in" и сделать это:

enum Options {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three',
}
interface OptionRequirement {
  someBool: boolean;
  someString: string;
}
type OptionRequirements = {
  [key in Options]: OptionRequirement; // Note that "key in".
}
Начо Юстисия Рамос
источник
5
Эмм, это не компилируется? Машинопись площадка говорит : «Вычисленное имя свойства в интерфейсе должен ссылаться на выражение, тип которого является буквальным типа или типа„уникальный символ“.»
meriton
19
Изменить interface OptionRequirementsнаtype OptionRequirements
Тайлер Рик
3
на самом деле это не работает для меня: имя вычисляемого свойства в интерфейсе должно ссылаться на выражение, тип которого является буквальным типом или типом «уникальный символ»
Tyguy7
Не могли бы вы рассказать нам, какую версию Typescript вы используете?
Начо
2
Я отредактировал этот ответ, чтобы использовать псевдоним сопоставленного типа вместо интерфейса. Исходный ответ не компилируется ни в одной из версий TypeScript, которые я видел, и, конечно же, не компилируется в текущей версии TypeScript (4.0 по состоянию на август 2020 г.). @NachoJusticiaRamos, если бы вы могли продемонстрировать, что ваша исходная версия действительно где-то работает, в какой-то версии TypeScript, я был бы счастлив отменить редактирование вместе с описанием среды, которую вам нужно использовать, чтобы она работала. Ура!
jcalz
58

Самое простое решение - использовать Record

type OptionRequirements = Record<Options, OptionRequirement>

Вы также можете реализовать это самостоятельно как:

type OptionRequirements = {
  [key in Options]: OptionRequirement;
}

Эта конструкция доступна только для type, но не доступна interface.

Проблема в вашем определении заключается в том, что ключ вашего интерфейса должен иметь тип Options, где Optionsэто перечисление, а не строка, число или символ.

В key in Optionsсредства «для этих конкретных ключей , которые в параметрах типа союза».

typealias более гибкий и мощный, чем interface.

Если ваш тип не должен использоваться в классе, выбрать typeболее interface.

униональный
источник
5

У меня была аналогичная проблема, но в моем случае было другое свойство поля в интерфейсе, поэтому мое решение в качестве примера с необязательным свойством поля с перечислением для ключей:

export enum ACTION_INSTANCE_KEY {
  cat = 'cat',
  dog = 'dog',
  cow = 'cow',
  book = 'book'
}

type ActionInstances = {
  [key in ACTION_INSTANCE_KEY]?: number; // cat id/dog id/cow id/ etc // <== optional
};

export interface EventAnalyticsAction extends ActionInstances { // <== need to be extended
  marker: EVENT_ANALYTIC_ACTION_TYPE; // <== if you wanna add another field to interface
}
Курков Игорь
источник
4

Вместо использования интерфейса используйте сопоставленный тип объекта

enum Option {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three'
}

type OptionKeys = keyof typeof Option;

interface OptionRequirement {
  someBool: boolean;
  someString: string;
}

type OptionRequirements = {                 // note type, not interface
  [key in OptionKeys]: OptionRequirement;   // key in
}
Стефан
источник
1

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

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
        }
    }
}
Аладзави
источник
0

У меня была аналогичная проблема. Я пытался использовать только определенные ключи при создании валидаторов угловой формы.

export enum FormErrorEnum {
  unknown = 'unknown',
  customError = 'customError',
}

export type FormError = keyof typeof FormErrorEnum;

И использование:

static customFunction(param: number, param2: string): ValidatorFn {
  return (control: AbstractControl): { [key: FormErrorEnum]?: any } => {
    return { customError: {param, param2} };
  };
}

Это позволит использовать 1 - X количество ключей.

Допрос
источник