javascript - Versiune sigură din punct de vedere al tipurilor a lodash _.get - deconstruiește tipul array condiționat
De mult timp avem o problemă: singura modalitate de a accesa în siguranță și ușor o proprietate imbricată este să folosim _.get. De exemplu:
_.get(obj, "Some.Nested[2].Property", defaultValue);
Funcționează foarte bine, dar nu rezistă schimbărilor de nume ale proprietăților așa cum se întâmplă frecvent. Teoretic, ar trebui să fie posibil să transformi ceea ce este de mai sus în următoarele și să permiți TypeScript să verifice implicit tipul:
safeGet(obj, "Some", "Nested", 2, "Property", defaultValue);
Am avut succes în crearea unui astfel de tip pentru tot, cu excepția tipurilor de array:
function getSafe<TObject, P1 extends keyof TObject>(obj: TObject, p1: P1): TObject[P1];
function getSafe<TObject, P1 extends keyof TObject, P2 extends keyof TObject[P1]>(obj: TObject, p1: P1, p2: P2): TObject[P1][P2];
Aceasta verifică corect elementele la adâncime (voi genera automat aceste declarații până la aproximativ 10 niveluri). Eșuează cu proprietățile array pentru că tipul transmis parametrului următor este T[] și nu T.
Complexitatea sau verbozitatea oricărei soluții nu contează, deoarece codul va fi generat automat, problema este că nu pot să găsesc nicio combinație de declarații de tip care să-mi permită să accept un parametru de tip întreg și să deconstruiesc tipul de array pe viitor.
Poți deconstrui un array (unde T este un array) folosind T[number]. Problema este că nu am nicio modalitate de a restricționa unde T este un array pe o proprietate imbricată.
function getSafe<TObject, P1 extends keyof TObject, P2 extends keyof TObject[P1][number]>(obj: TObject, p1: P1, index: number, p2: P2): TObject[P1][number][P2];
^^^^^^ ^^^^^^
const test2 = getSafe(obj, "Employment", 0, "Id"); // example usage
Acesta funcționează efectiv la locul apelului (fără erori acolo, ne oferă corect tipurile parametrilor și ale returului), dar ne dă o eroare în declarația propriu-zisă deoarece nu poți indexa TObject[P1] cu [number] întrucât nu putem garanta că TObject[P1] este un array.
(Notă: TType[number] este o modalitate viabilă de a obține tipul elementului dintr-un tip de array, dar trebuie să convingem compilatorul că facem acest lucru asupra unui array)
Întrebarea este, de fapt, dacă ar exista o modalitate de a adăuga o constrângere de tablou la TObject[P1] sau dacă există o altă metodă de a face asta pe care o ratez.
Răspuns #1
Între timp am descoperit acest lucru și am publicat un pachet npm aici: ts-get-safe
Punctul cheie a fost să descoperim cum să restructurăm condiționat array-ul în tipul său de element. Pentru a face asta, trebuie mai întâi să afirmi că toate proprietățile sunt fie un array, fie never. Tipul care a rezolvat ecuația a fost:
type GSArrEl<TKeys extends keyof TObj, TObj> = { [P in TKeys]: undefined[] & TObj[P] }[TKeys][number];
Magia este în { [P in TKeys]: undefined[] & TObj[P] } unde practic reunim fiecare proprietate a TObj cu undefined[]. Deoarece suntem apoi siguri că fiecare proprietate este fie un array, fie never (va fi never pentru fiecare proprietate care nu este un array), putem apoi să folosim expresia de destructurare [number] pentru a obține tipul elementului.
Iată un exemplu de destructurare a două array-uri care se întâmplă în același timp:
function getSafe<TObject, P0 extends keyof TObject, A1 extends GSArrEl<P0, TObject>, P2 extends keyof A1, P3 extends keyof A1[P2], A4 extends GSArrEl<P3, A1[P2]>>(obj: TObject, p0: P0, a1: number, p2: P2, p3: P3, a4: number): A4;
Sute de combinații de array-uri și proprietăți de obiect au fost generate în biblioteca mea ts-get-safe și sunt gata de utilizare, totuși sunt încă deschis la modalități de a îmbunătăți acest lucru într-un mod generic astfel încât să putem folosi un număr dinamic de parametri în aceeași declarație. Chiar și o modalitate de a combina navigarea prin Array și proprietăți într-o singură constrângere de tip, astfel încât să nu trebuiască să generăm fiecare variație de acces la array și proprietăți.
