Typ parametru podpisu indeksu nie może być typem unii. Zamiast tego rozważ użycie mapowanego typu obiektu

131

Próbuję użyć następującego wzoru:

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

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

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

Wydaje mi się to bardzo proste, jednak pojawia się następujący błąd:

Typ parametru podpisu indeksu nie może być typem unii. Zamiast tego rozważ użycie mapowanego typu obiektu.

Co ja robię źle?

john maccarthy
źródło
1
Typ keymoże być tylko ciągiem znaków, liczbą lub symbolem. enum nie jest.
unional

Odpowiedzi:

197

Możesz użyć operatora TS „in” i zrobić to:

enum Options {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three',
}
interface OptionRequirement {
  someBool: boolean;
  someString: string;
}
type OptionRequirements = {
  [key in Options]: OptionRequirement; // Note that "key in".
}
Nacho Justicia Ramos
źródło
7
Erm, to się nie kompiluje? Maszynopis zabaw mówi : „obliczony nazwa właściwości w interfejs musi odnosić się do wypowiedzi, której typ jest dosłownym typ lub«wyjątkowy symbol»typ”.
meriton
19
Zmień interface OptionRequirementsnatype OptionRequirements
Tyler Rick
4
to faktycznie nie działa dla mnie: obliczona nazwa właściwości w interfejsie musi odnosić się do wyrażenia, którego typ jest typem literału lub typem „unikalnego symbolu”
Tyguy7
Czy możesz nam powiedzieć, jakiej wersji maszynopisu używasz?
Nacho Justicia Ramos
2
Zedytowałem tę odpowiedź, aby użyć zmapowanego aliasu typu zamiast interfejsu. Oryginalna odpowiedź nie kompiluje się pod żadną wersją TypeScript, którą widziałem, a na pewno nie kompiluje się pod bieżącą wersją TypeScript (4.0 od sierpnia 2020 r.). @NachoJusticiaRamos, gdybyś mógł wykazać, że Twoja oryginalna wersja rzeczywiście gdzieś działa, w jakiejś wersji TypeScript, z przyjemnością cofnę edycję wraz z opisem środowiska, którego potrzebujesz, aby działała. Twoje zdrowie!
jcalz
66

Najprostszym rozwiązaniem jest użycie Record

type OptionRequirements = Record<Options, OptionRequirement>

Możesz również zaimplementować to samodzielnie jako:

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

Ta konstrukcja jest dostępna tylko dla type, ale nie interface.

Problem w twojej definicji polega na tym, że klucz twojego interfejsu powinien być typu Options, gdzie Optionsjest wyliczeniem, a nie ciągiem, liczbą lub symbolem.

Te key in Optionsśrodki „dla tych konkretnych klawiszy, które w Opcjach Union Typ”.

typealias jest bardziej elastyczny i potężniejszy niż interface.

Jeśli Twój typ nie musi być używany na zajęciach, wybierz typeinny interface.

unional
źródło
8

Miałem podobny problem, ale mój przypadek dotyczył innej właściwości pola w interfejsie, więc moje rozwiązanie jako przykład z opcjonalną właściwością pola z wyliczeniem dla kluczy:

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
}
Kurkov Igor
źródło
5

Zamiast używać interfejsu, użyj odwzorowanego typu obiektu

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
}
Stefan
źródło
3

W moim przypadku potrzebowałem, aby właściwości były opcjonalne, więc utworzyłem ten typ ogólny.

type PartialRecord<K extends string | number | symbol, T> = { [P in K]?: T; };

Następnie użyj go jako takiego:

type MyTypes = 'TYPE_A' | 'TYPE_B' | 'TYPE_C';

interface IContent {
    name: string;
    age: number;
}

interface IExample {
    type: string;
    partials: PartialRecord<MyTypes, IContent>;
}

Przykład

const example : IExample = {
    type: 'some-type',
    partials: {
        TYPE_A : {
            name: 'name',
            age: 30
        },
        TYPE_C : {
            name: 'another name',
            age: 50
        }
    }
}
Alazzawi
źródło
0

Miałem podobny problem. Próbowałem używać tylko określonych kluczy podczas tworzenia walidatorów kątowych.

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

export type FormError = keyof typeof FormErrorEnum;

I użycie:

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

Pozwoli to na użycie 1 - X liczby kluczy.

Pytający
źródło