Typescript Typ Objekt verschachtelter Pfad String
Ich habe die folgende get-Funktion:
function get<T>(obj: T, props: (keyof T)[] | keyof T): any {
const toArray = coereceArray(props);
return obj && toArray.reduce(
(result, prop) => result == null ? undefined : result[prop] as any,
obj
);
}
const result = get({ a: { b: 'hey' } }, ['a', 'b']);
const result2 = get({ a: { b: 'hey' } }, 'a');
Wie kann ich das Ergebnis basierend auf den übergebenen Parametern dynamisch eingeben?
Antwort #1
Sehr interessanter Fall. Um das Sammeln aller Schlüssel auch aus verschachtelten Objekten durchzuführen, müssen wir Rekursion verwenden, zum Glück sind rekursive Typen in TypeScript verfügbar. Aber wenn wir dies tun, haben wir ein zusätzliches Problem, und zwar genau - Die Typinstanziierung ist übermäßig tief und möglicherweise unendlich, da wir in diesem konkreten Fall nicht sagen können, wie tief das Objekt sein kann. Überprüfen Sie dieses Problem, um zu verstehen, wovon ich spreche - issue
Um unsere Zusammenstellung durchzuführen, müssen wir dort ein Rekursionslimit setzen. Ich habe das Limit auf 5 gesetzt, Sie können es erweitern, indem Sie das Muster wiederholen.
Zuerst der Typ:
type Level = 0 | 1 | 2 | 3 | 4 | 5 | 'max'; // this solves the infinite loop error issue
type NextLevel<Level> =
Level extends 0 ? 1
: Level extends 1 ? 2
: Level extends 2 ? 3
: Level extends 3 ? 4
: Level extends 4 ? 5
: 'max'; // this type enables iterating from 0 to 5, with the end as 'max'
type NestedKeyof<T, L extends Level = 0> = L extends 'max' ? never : {
[K in keyof T]: T[K] extends object ? K | NestedKeyof<T[K], NextLevel<L>> : K
}[keyof T]
Der Typ NestedKeyof ist ein gemappter Typ, der alle Schlüssel in der Map sammelt, aber wenn der Wert des Schlüssels ein Objekt ist, dann wird die Sammlung auch für dieses Objekt durchgeführt. Wichtig ist K | NestedKeyof<T[K], NextLevel<L>>, sodass wir rekursiv das Objekt T[K] übergeben und die Ebene mit NextLevel<L> iterieren. Die Iteration wird mit dem Typ max beendet, was zu Beginn des Typs zu sehen ist - L extends 'max' ? never.
Ein einfacher Test des Typs zeigt die richtigen Ergebnisse:
type NestedType = NestedKeyof<{ a: { b: 'hey', c: { d: 'elo' } } }>; // a | b | c | d
Jetzt, wie man es mit deiner Funktion benutzt:
function get<T, P extends NestedKeyof<T>>(obj: T, props: P[] | keyof T): any {
const toArray = coereceArray(props);
return obj && toArray.reduce(
(result, prop) => result == null ? undefined : result[prop] as any,
obj
);
}
const result = get({ a: { b: 'hey' } }, ['a', 'b']); // correct
const result2 = get({ a: { b: 'hey' } }, 'a'); // correct
const result3 = get({ a: { b: 'hey' } }, ['a', 'b', 'c']); // error as expected
Der Typ NestedKeyof hat Einschränkungen, da ich Level und NextLevel für maximal 5 Ebenen von verschachtelten Objekten erstellt habe. Zum Beispiel ein solcher Typ:
type NestedTypeAboveMax = NestedKeyof
<{ a: { b: 'hey', c: { d: 'elo', e: { f: { g: { h: {i: 'test'}} } } } } }>;
Der Schlüssel i wird nicht übernommen, da er außerhalb des Limits liegt. Wenn jedoch solche verschachtelten Strukturen verwendet werden, können Sie Level und NextLevel erweitern, um in der Lage zu sein, mehr Ebenen zu haben.
