Тип параметра підпису індексу не може бути типом об’єднання. Подумайте про використання натомість об’єктного типу


102

Я намагаюся використовувати такий шаблон:

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

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

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

Мені це здається дуже простим, однак я отримую таку помилку:

Тип параметра підпису індексу не може бути типом об’єднання. Подумайте про використання натомість відображеного типу об’єкта.

Що я роблю не так?


1
Тип keyможе бути лише рядком, номером або символом. перелік не є.
профспілка

Відповіді:


158

Ви можете використовувати оператор 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
Е-е, це не компілюється? На дитячому майданчику TypeScript сказано : "Обчислене ім’я властивості в інтерфейсі повинно посилатися на вираз, тип якого є літеральним типом або типом" унікального символу "."
meriton

19
Перехід interface OptionRequirementsнаtype OptionRequirements
Тайлер Рік

3
це насправді не працює для мене: обчислене ім'я властивості в інтерфейсі повинно посилатися на вираз, тип якого є літеральним типом або типом "унікального символу"
Tyguy7

Не могли б ви сказати нам, яку версію Typescript ви використовуєте?
Nacho Justicia Ramos

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"для тих конкретних ключів, які знаходяться в параметрі типу об'єднання".

typeПсевдонім є більш гнучким і потужним, ніж 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 кількість клавіш.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.