Ein Indexsignaturparametertyp kann kein Vereinigungstyp sein. Verwenden Sie stattdessen einen zugeordneten Objekttyp

104

Ich versuche das folgende Muster zu verwenden:

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

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

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

Dies scheint mir sehr einfach zu sein, ich erhalte jedoch den folgenden Fehler:

Ein Indexsignaturparametertyp kann kein Vereinigungstyp sein. Verwenden Sie stattdessen einen zugeordneten Objekttyp.

Was mache ich falsch?

John Maccarthy
quelle
1
Der Typ von keykann nur eine Zeichenfolge, eine Zahl oder ein Symbol sein. Aufzählung ist nicht.
unional

Antworten:

160

Sie können den TS "in" -Operator verwenden und dies tun:

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
quelle
5
Ähm, das kompiliert nicht? Auf dem TypeScript-Spielplatz heißt es : "Ein berechneter Eigenschaftsname in einer Schnittstelle muss sich auf einen Ausdruck beziehen, dessen Typ ein Literaltyp oder ein 'eindeutiger Symbol'-Typ ist."
Meriton
19
Wechseln Sie interface OptionRequirementszutype OptionRequirements
Tyler Rick
3
Das funktioniert bei mir eigentlich nicht: Ein berechneter Eigenschaftsname in einer Schnittstelle muss sich auf einen Ausdruck beziehen, dessen Typ ein
Literaltyp
Können Sie uns bitte mitteilen, welche Version von Typescript Sie verwenden?
Nacho Justicia Ramos
2
Ich habe diese Antwort bearbeitet, um einen zugeordneten Typalias anstelle einer Schnittstelle zu verwenden. Die ursprüngliche Antwort wird unter keiner Version von TypeScript kompiliert, die ich gesehen habe, und sicherlich nicht unter der aktuellen Version von TypeScript (4.0 ab August 2020). @NachoJusticiaRamos, wenn Sie nachweisen könnten, dass Ihre Originalversion in einer TypeScript-Version tatsächlich irgendwo funktioniert, würde ich die Bearbeitung gerne zurücksetzen, zusammen mit einer Beschreibung der Umgebung, die Sie verwenden müssen, damit sie funktioniert. Prost!
Jcalz
58

Die einfachste Lösung ist die Verwendung Record

type OptionRequirements = Record<Options, OptionRequirement>

Sie können es auch selbst implementieren als:

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

Dieses Konstrukt steht nur zur Verfügung type, aber nicht interface.

Das Problem in Ihrer Definition besteht darin, dass der Schlüssel Ihrer Schnittstelle vom Typ sein sollte Options, wobei Optionses sich um eine Aufzählung handelt, nicht um eine Zeichenfolge, eine Zahl oder ein Symbol.

Das key in Optionsbedeutet "für die spezifischen Schlüssel, die in den Union-Typ-Optionen enthalten sind".

typeAlias ​​ist flexibler und leistungsfähiger als interface.

Wenn Ihr Typ nicht im Unterricht verwendet werden muss, wählen typeSie aus interface.

unional
quelle
5

Ich hatte ein ähnliches Problem, aber mein Fall war mit einer anderen Feldeigenschaft in der Schnittstelle, so dass meine Lösung als Beispiel mit einer optionalen Feldeigenschaft mit einer Aufzählung für Schlüssel:

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
quelle
4

Verwenden Sie anstelle einer Schnittstelle einen zugeordneten Objekttyp

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
quelle
1

In meinem Fall mussten die Eigenschaften optional sein, also habe ich diesen generischen Typ erstellt.

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

Dann benutze es als solches:

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

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

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

Beispiel

const example : IExample = {
    type: 'some-type',
    partials: {
        TYPE_A : {
            name: 'name',
            age: 30
        },
        TYPE_C : {
            name: 'another name',
            age: 50
        }
    }
}
Alazzawi
quelle
0

Ich hatte ein ähnliches Problem. Ich habe versucht, beim Erstellen von Formularprüfern nur bestimmte Schlüssel zu verwenden.

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

export type FormError = keyof typeof FormErrorEnum;

Und die Verwendung:

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

Dadurch kann 1 - X Anzahl von Schlüsseln verwendet werden.

Befragung
quelle