Typescript: Schnellkorrektur für "Gewerkschaften können nicht in Indexsignaturen verwendet werden, verwenden Sie stattdessen einen zugeordneten Objekttyp"

Erstellt am 17. Mai 2018  ·  37Kommentare  ·  Quelle: microsoft/TypeScript

Der folgende Code:

type K = "foo" | "bar";

interface SomeType {
    [prop: K]: any;
}

Gibt diese Fehlermeldung aus:

An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

Niemand weiß, was zugeordnete Objekttypen sind, also geben wir ihnen eine schnelle Lösung dafür

  • Schaltet die Indexsignatur auf einen zugeordneten Typ um
  • Verschiebt andere Elemente in einen separaten Objekttyp, der mit einem Schnittpunkttyp kombiniert wird
  • Ändert den enthaltenen Objekttyp in einen Typalias, wenn der enthaltende Objekttyp eine Schnittstelle ist
  • Überschneidet sich mit allen extends -Klauseln, wenn der enthaltende Objekttyp eine Schnittstelle ist und extends -Klauseln enthält
Error Messages Quick Fixes Moderate Fixed Suggestion help wanted

Hilfreichster Kommentar

Du kannst das:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Obwohl Bar keine Indexsignatur hat (dh Sie können dann nicht (obj as Bar)[value as Foo] tun).

Bearbeiten: Obwohl, wenn Sie die Einschränkung zu einem Nicht-Problem machen könnten, wäre ich auf ewig dankbar!

Alle 37 Kommentare

Niemand weiß, was zugeordnete Objekttypen sind, also geben wir ihnen eine schnelle Lösung dafür

+1, bin gerade hierher gekommen, weil ich erwartet hatte, dass 2.9 Gewerkschaften als Indexsignaturen gemäß Ihrem Beispielcode unterstützt. Ich denke, dies war eine lange gewünschte Funktion: # 5683, # 16760, etc ..

Du kannst das:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Obwohl Bar keine Indexsignatur hat (dh Sie können dann nicht (obj as Bar)[value as Foo] tun).

Bearbeiten: Obwohl, wenn Sie die Einschränkung zu einem Nicht-Problem machen könnten, wäre ich auf ewig dankbar!

Daran möchte ich arbeiten: lachen:

Verschiebt andere Elemente in einen separaten Objekttyp, der mit einem Schnittpunkttyp kombiniert wird

Was sollen wir tun, wenn der Objekttyp eine Klasse ist?
Ich kann mir nur vorstellen, dass es sich um eine Schnittstelle handelt

Was sollte der folgende Code nach dem Quickfix tun?

type K = "1" | "2"

class SomeType {
    a = 1;
    [prop: K]: any;
}

Was sollte der folgende Code nach dem Quickfix tun?

Ich würde sagen, das sollte nicht reparabel sein ..

@mhegazy Ich verwende 3.0.0-rc und

Ich verwende 3.0.0-rc und erhalte immer noch den gleichen Fehler wie ursprünglich veröffentlicht. Wird das erwartet?

Ja. Der Fehler ist korrekt. Bei diesem Problem wurde nachverfolgt, wie eine schnelle Lösung hinzugefügt wurde, dh der Lichtstoff neben der Fehlermeldung.

Mit 2.9.1 und vscode sind keine Codeaktionen verfügbar

@ThaJay Wir werden diese Funktion nicht , eine neuere Version

Offensichtlich. Es tut mir leid, dass ich die Zeitleiste nicht zuerst überprüft habe, sondern nur angenommen habe, dass sie neu genug ist. Neu bei ts. Wird mit Version 3 überprüfen.

wie man das beschreibt:

function createRequestTypes(base){
  return ['REQUEST', 'SUCCESS', 'FAILURE'].reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, {})
}

const user = createRequestTypes('USER')
console.log(user.REQUEST) // error
// just string? like:
interface IRequestType: {[key: string]: string}

Ich habe es unten versucht, alles ist fehlgeschlagen:

type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = {
  [key in requestStatus]: string
}
// or
interface IRequestTypes {[key: keyType]: string}
// or even
type requestTypes = {
  FAILURE: string,
  SUCCESS: string,
  REQUEST: string
}

@maicWorkGithub los geht's:

const user = createRequestTypes('USER')
console.log(user.REQUEST) 

function createRequestTypes(base:string):requestTypes {
  const result : requestTypes    = {}
  const arr    : requestStatus[] = ['REQUEST', 'SUCCESS', 'FAILURE']  

  return arr.reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, result)
}


type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = { [key in requestStatus]?: string }

@ihorskyi Danke !!

Nur neugierig, warum type funktioniert, aber interface nicht. Kann mir bitte jemand erklären? Was ist der Grund für eine solche Einschränkung (oder eine Funktion?) Von interface .

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any}; // ok
interface Baz {[key in Foo]: any} // =>

// A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
// A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)
// 'Foo' only refers to a type, but is being used as a value here.ts(2693)

Dies war eine erstaunliche automatische Korrektur zu entdecken. Vielen Dank für die Implementierung! :) :)

Gleiches gilt für Klassen.

Du kannst das:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Obwohl Bar keine Indexsignatur hat (dh Sie können dann nicht (obj as Bar)[value as Foo] tun).

Bearbeiten: Obwohl, wenn Sie die Einschränkung zu einem Nicht-Problem machen könnten, wäre ich auf ewig dankbar!

Verwenden Sie stattdessen Record !

type Foo = 'a' | 'b'
type Bar = Record<Foo, any>

Um ein weiteres Beispiel dafür mit einer Klasse hinzuzufügen ...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

Es ist sogar noch besser, wenn Sie Partial verwenden

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

Niemand weiß, was zugeordnete Objekttypen sind, also geben wir ihnen eine schnelle Lösung dafür

@ DanielRosenwasser
Warum kann die Fehlermeldung nicht die Antwort vorschlagen, z. B. ein kurzes Beispiel mit zugeordnetem Typ zeigen - das wären nur ein paar Codezeilen, die mit der durchschnittlichen Länge von Typescript-Fehlermeldungen übereinstimmen: trollface:

Weiß jemand, ob es möglich ist zu sagen, dass die Schnittstelle, die den Typ oder die Aufzählung als Schlüssel verwendet, nur eine Eigenschaft akzeptieren kann?

Zum Beispiel eine Signatur wie diese: { <field>: { <and|or|xor>: <int> } } vom mongo bitweisen Operator übernommen .

export enum BitwiseOperator {
    and = "and",
    or = "or",
    xor = "xor",
}

export type BitwiseCondition = {
    [key in BitwiseOperator]?: number;
}

Wenn ich es dann benutze, möchte ich überprüfen, ob die Variable, die von der Schnittstelle definiert wird, nur eine Eigenschaft hat.

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

@ b4dnewz In Typescript ist das nicht möglich. Problemumgehung: https://github.com/Microsoft/TypeScript/issues/10575

@ b4dnewz , wenn du nur 1 Eigenschaft willst, warum machst du es nicht so?

export enum BitwiseOperator {
  and = "and",
  or = "or",
  xor = "xor",
}

export type BitwiseCondition = {
  operator: BitwiseOperator;
  value: number;
}

@benwinding leider unterscheidet sich die zurückgegebene Form von dem, was mongodb erwartet

@apieceofbart danke für den Vorschlag, ich habe ihn mir angesehen, ein bisschen redundant in Bezug auf Schnittstellen, kann aber funktionieren, ich bin mir nicht sicher, ob ich ihn jetzt implementieren werde, da es keine große Sache ist, wenn der Endbenutzer einen versucht bitweise Bedingung mit zwei Operatoren, Mongo wird trotzdem einen Fehler auslösen

Ich versuche, die Definitionen der Mongo-Operatoren so einfach wie möglich zu halten, um Kopfschmerzen zu vermeiden. Vielleicht wird in Zukunft eine angemessene Unterstützung hinzugefügt

@ b4dnewz fair genug,

Eine einfachere Option, die Sie möglicherweise verwenden können, ist:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

Das ist ungefähr das Nächste, was Sie ohne zu viel Doppelarbeit erreichen

@ b4dnewz fair genug,

Eine einfachere Option, die Sie möglicherweise verwenden können, ist:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

Das ist ungefähr das Nächste, was Sie ohne zu viel Doppelarbeit erreichen

Dies führt in diesem Beispiel nicht zu Fehlern:

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

Ich dachte, das ist der springende Punkt

@apieceofbart ,

Dies führt in diesem Beispiel nicht zu Fehlern:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

const query: BitwiseCondition = {
  and: 5,
  or: 6  // doesn't raise a ts error!
};

Woah! das ist komisch: open_mouth: das wusste ich nicht!

Es scheint, dass Typescript keine sich gegenseitig ausschließenden Typen für Objekte unterstützt. Es war auch ein Vorschlag für die Sprache hier: https://github.com/microsoft/TypeScript/issues/14094

Es ist aber technisch immer noch möglich ...

Aus dieser Stackoverflow- Antwort geht hervor, dass dies mit bedingten Typen (den schwierigsten Typen) erreicht werden kann, aber es ist nicht schön ...

/*
 XOR boiler plate
*/
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = T | U extends object
  ? (Without<T, U> & U) | (Without<U, T> & T)
  : T | U;
type XOR3<S, T, U> = XOR<S, XOR<T, U>>;

// Code start
export type BitwiseCondition = XOR3<
  { or: number },
  { xor: number },
  { and: number }
>;

const query1: BitwiseCondition = {
  and: 5
};

const query: BitwiseCondition = {
  and: 5,
  or: 6 // raise a ts error
};

Wenn jemand dies schöner oder besser machen könnte, tun Sie es bitte

@mvasin FWIW, dies scheint das gleiche Ergebnis zu erzielen, aber ich stimme voll und ganz zu, dass es ein Merkmal von Schnittstellen sein sollte, genau wie es auf Typen ist.

type Foo = 'a' | 'b';

type Bar = {
  [key in Foo]: any
}

interface A extends Bar { }

class Wol implements A{
  a: any;
  b: any;
}

Für Typoskript 3.5 muss ich anscheinend Folgendes tun:

export interface DataTableState {
  columnStats: {[key in keyof DataTable]?:{}}
}

Ist das der beste Weg, dies zu tun?

Warum kann eine Indexsignatur

Zum Beispiel für den Typ:

type MyType = {
  [Key: 'foo' | 'bar' | 'zip']: number;
};

Dies sollte befriedigen:

const x: MyType = {
  foo: 1,
  zip: 2
};

Während ich die anderen Schlüssel für einen zugeordneten Typ einfach undefiniert setzen könnte, ziehe ich es vor, die Schlüssel optional zu machen, aber wenn sie vorhanden sind, kann der Wert nicht undefiniert sein. Wenn ich die zugeordneten Typwerte optional mache, funktioniert der Code, aber die Typen sind weniger stark.

Es ist sogar noch besser, wenn Sie Partial verwenden

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

Vielen Dank!
Nützlich, wenn Sie einen Typ definieren müssen, der teilweise mit einem Wörterbuch übereinstimmt

"Partial" kann auch für Datensätze verwendet werden:

type Foo = 'a' | 'b';
let foo1: Record<Foo, number> = { a: 1, b: 2 };
let foo2: Partial<Record<Foo, number>> = { a: 1 };

Ich besuche diese GitHub-Seite jeden Monat oder so unabsichtlich.

Mein letztes ist ganz einfach:

interface ObjectLiteral {
    [key: string | number]: any
}
export const mapToObjectLiteral = (map: Map<string|number, any>) =>
    Array.from(map).reduce((objLit, [key, value]) => {
        objLit[key] = value
        return objLit
    }, {} as ObjectLiteral)

image

Ich kann nach oben scrollen und eine Problemumgehung finden, wollte aber nur Feedback geben, dass dieses Problem bei der täglichen Arbeit in etwas anderen Szenarien häufig auftritt.

Hier ist ein Beispiel:

type MapKey = string | number;
type ObjectLiteral<T extends MapKey, V = any> = {
  [P in T extends number ? string : T]: V;
};

export const mapToObjectLiteral = <T extends MapKey, V>(map: Map<T, V>) =>
  Array.from(map).reduce((objLit, [key, value]) => {
    objLit[key as keyof ObjectLiteral<T>] = value;
    return objLit;
  }, {} as ObjectLiteral<T, V>);

// how to make a better type of map ?
const m = new Map<1 | "foo", "a" | "b">();
m.set(1, "a");
m.set("foo", "b");

const o = mapToObjectLiteral(new Map(m));

console.log(o[1], o.foo); // just got an union type of every member of 'o'

https://github.com/microsoft/TypeScript/issues/24220#issuecomment -504285702

Um ein weiteres Beispiel dafür mit einer Klasse hinzuzufügen ...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

Sehr hilfreich. Vielen Dank! 🚀

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen