Typoskript: Leiten Sie den Vereinigungstyp aus einem Array von Objekten ab

8

Ich möchte ein typerzwungenes Array von Elementen deklarieren und daraus einen Unionstyp ableiten können. Dieses Muster funktioniert, wenn Sie den Elementen im Array keinen expliziten Typ zuweisen. Ich bin mir nicht sicher, wie ich es am besten erklären soll. Hier ist ein Beispiel:

BEISPIEL 1

type Pair = {
  key: string;
  value: number;
};

const pairs: ReadonlyArray<Pair> = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']

BEISPIEL 2

type Data = {
  name: string;
  age: number;
};

const DataRecord: Record<string, Data> = {
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
} as const;

type Keys = keyof typeof DataRecord;

Hier ist ein Beispiel für das Ableiten der Schlüssel bei der Verwendung as const. Ich möchte dasselbe Verhalten, aber das Array wird explizit eingegeben.

const pairs = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']; // "foo" | "bar"

gewünschter Wert der Schlüssel: "foo"|"bar"

Istwert der Schlüssel: string

Ben
quelle
4
Ich glaube nicht, dass Sie dies dynamisch so tun können, wie Sie es versuchen. Sie kombinieren Laufzeitwerte mit Typen zur Kompilierungszeit. Sie müssen dem keyAttribut des PairTyps den gewünschten Typ geben, dann sollte es so funktionieren, wie Sie es geschrieben haben.
Jared Smith
4
Beantwortet das deine Frage? Typoskript leitet
Unionstyp aus Tupel-
@JaredSmith Dies sollte zur Laufzeit kein Problem sein. Ich verwende dies, um eine beliebige Anzahl von Werten zu deklarieren, die sich während der Ausführung nicht ändern. Dies entspricht der Einstellung von Schlüssel: "foo" | "bar" in der Typdeklaration.
Ben
„Das sollte kein Problem sein , zur Laufzeit“ --- Typoskript nicht Laufzeit hat, so dass es ist ein Problem , es im laufenden Betrieb zu tun.
Zerkms
1
@Ben erlaubt mir, genauer zu sein: Ich glaube nicht, dass Sie dies mit einem Tupel von Eigenschaften tun können, die aus veränderlichen Referenztypen herausgezogen wurden, wie Sie es mit einem Tupel unveränderlicher Grundelemente tun können. Sie können sagen, possibleKeys = ['foo', 'bar'] as const; type Keys = typeof possibleKeys[number]; type Pair = { key: Keys, value: number };aber Sie müssen die möglichen Schlüssel noch explizit auflisten.
Jared Smith

Antworten:

3

Für eine Variable können Sie entweder den Compiler den Typ aus der Initialisierung ableiten lassen oder ihn explizit ausschreiben. Wenn Sie es explizit schreiben, wie Sie es getan haben, wird der Initialisierungswert mit der Annotation verglichen, aber der tatsächliche Typ des Initialisierers hat keinen Einfluss auf den Typ der Variablen (sodass Sie die gewünschten Typinformationen verlieren). Wenn Sie den Compiler darauf schließen lassen, ist es nicht mehr möglich, den Typ so zu beschränken, dass er einer bestimmten Schnittstelle entspricht (wie Sie es zu wollen scheinen).

Die Lösung hierfür besteht darin, eine generische Funktion zu verwenden, um den Wert zu beschränken und auf den tatsächlichen Typ zu schließen:

type Pair = {
  key: string;
  value: number;
};
function craetePairsArray<T extends readonly Pair[] & Array<{key: V}>, V extends string>(...args: T) {
    return args
}

const pairs = craetePairsArray(
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
)

type Keys1 = typeof pairs[number]['key']

type Data = {
  name: string;
  age: number;
};

function craeteDataObject<T extends Record<string, Data>>(arg: T) {
    return arg;
}
const DataRecord = craeteDataObject({
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
})

type Keys2 = keyof typeof DataRecord;

Spielplatz Link

Hinweis: Für den Array-Fall müssen wir den Compiler ein wenig scharf schalten, um auf String-Literal-Typen für keydaher das Ganze zu schließen & Array<{key: V}>, wobei Vsich ein Typparameter erweitertstring

Tizian Cernicova-Dragomir
quelle
1
Vielen Dank! Genau das brauchte ich!
Ben
2

Die üblichen Ansätze sind:

  • Lassen Sie TS den Typ von ableiten, pairsindem Sie den expliziten Typ weglassen ReadonlyArray<Pair>(siehe Antwort ).
  • geben keyin Pairder Art"foo"|"bar"

Wenn Sie dies nicht möchten, können Sie Ihre Schlüssel nur ableiten und den Typ einschränken , indem Sie pairseine Hilfsfunktion verwenden. Der PairTyp wird auch generisch gemacht, um die angegebenen keyString-Literal-Typen zu speichern . Sie können ein IIFE verwenden, um die Zuordnung kompakt zu gestalten:

type Pair<K = string> = {
    key: K;
    value: number;
};

const pairs = (<T>(p: readonly Pair<T>[]) => p)([
    { key: 'foo', value: 1 },
    { key: 'bar', value: 2 },
] as const) // readonly Pair<"foo" | "bar">[]

type Keys = typeof pairs[number]['key'] // "foo" | "bar"

Spielplatz

bela53
quelle