typescript - Dezvăluirea unui array 'as const' în echivalentul său mutabil, non-tuple
Am un array cu as const:
// just a simple mock class
class Child { _ = "" }
const child = new Child();
const schema = [[child], child] as const; // readonly [readonly [Child], Child];
Acest array reprezintă o uniune de tipuri, așadar aici, ar reprezenta un array de Child sau un alt array de Child (un nivel mai adânc). În esență, vreau să transform tipul lui schema în (Child | Child[])[].
type T = UnwrapAsConstArray<readonly [readonly [Child], Child]> // => (child | Child[])[]
Îmi este dificil să înțeleg logica necesară pentru a face această transformare posibilă. Aceasta este aici încercarea mea jalnică care, după cum se poate vedea la final, nu funcționează atât de bine.
Un teren de joacă unde poți încerca să găsești o soluție, cu câteva cazuri de test și comportament așteptat.
Vă rog să țineți cont că aș dori ca soluția să fie recursivă și să funcționeze pentru orice nivel de încuibare.
Întrebare similară: Creează un tuple de tip dintr-un array fără „as const”. Am nevoie de „opusul”, despre care știu că este posibil.
Răspuns #1
Abordarea mea aici ar arăta astfel:
type UnwrapAsConstArray<T> =
T extends readonly any[] ? UnwrapAsConstArray<T[number]>[] :
{ -readonly [K in keyof T]: UnwrapAsConstArray<T[K]> }
Acesta este foarte similar cu un tip simplu DeepMutable<T> de forma
type DeepMutable<T> = { -readonly [K in keyof T]: DeepMutable<T[K]> }
care va lăsa primitivele neschimbate și va elimina recursiv readonly din orice tipuri de obiect, lăsând array-urile ca array-uri și tuplele ca tuple. Singura diferență este verificarea pentru readonly any[], unde preluăm în mod explicit uniunea tipurilor elementelor sale T[number], aplicăm UnwrapAsConstArray pe acesta și producem un array obișnuit.
Asta trece toate testele din exemplul tău:
type UnwrappedSchema = UnwrapAsConstArray<typeof schema>;
// ^? type UnwrappedSchema = ({ _: string; } | { _: string; }[])[]
// not written as `(Child | Child[])[]`, but structrually identical
type Test01 = Assert<Equals<
UnwrappedSchema, (Child | Child[])[]>>; // okay
type Test02 = Assert<Equals<UnwrapAsConstArray<
readonly [readonly [1], 2]>, (2 | 1[])[] // okay
>>;
type Test03 = Assert<Equals<UnwrapAsConstArray<
readonly [1, readonly [2, 3], readonly [readonly [4, 5, 6]]]>,
(1 | (2 | 3)[] | (4 | 5 | 6)[][])[] // okay
>>;
type Test04 = Assert<Equals<UnwrapAsConstArray<
readonly []>, never[] // okay
>>;
type Test05 = Assert<Equals<UnwrapAsConstArray<
readonly [1]>, 1[] // okay
>>;
Rețineți bine că acest lucru va elimina recursiv caracterul de doar citire din orice tip de obiect, cum ar fi Child, pe care îl introduceți acolo și nu știe când să se oprească. Orice nu supraviețuiește unui mapare de tipuri va fi ciudat (tipurile de funcții vor deveni doar {}). Dacă trebuie să ajustați pentru aceste cazuri, va trebui să adăugați logică pentru a face acest lucru.
Link de teren de joacă către cod
