Typoskript 2D variadic Vorlage

Sep 25 2020

Ich habe derzeit eine Schnittstelle, die ein Datenelement eines Arrays eines anderen Schnittstellentyps verwendet, das durch eine Aufzählung bestimmt werden kann:

enum E { a, b, c }
interface E2Data {
    [E.a]: { a: string },
    [E.b]: { b: string },
    [E.c]: { c: string }
}

interface A<T extends E[] = E[]> {
    data: { [i in keyof T]: T[i] extends E ? E2Data[T[i]] : never }
}

// Can give the enums in the template, and get autocomplete in the data.
let a1: A<[E.a, E.b]> = {
    data: [{ a: "" }, { b: "" }]
};

// Or can get autocomplete with type assertion on elements.
// Ideally this would infer template types too, but apparently not.
let a2: A = {
    data: [ <E2Data[E.a]>{ a: "" }, <E2Data[E.b]>{ b: "" } ]
}

Nach meinem Verständnis kann ich jetzt a1.dataund a2.dataals Arrays verwenden, a1.datakenne aber den Typ jedes Elements (also eigentlich ein Tupel?)

Jetzt möchte ich ein 2D-Array für interface Adie Daten verwenden.

Ich dachte , das unten funktionieren würde, aber es würde mich nicht erlauben , zu indizieren E2Dataverwenden T[i][j].

interface B<T extends E[][] = E[][]> {
    data: {
        [i in keyof T]:
        T[i] extends E[] ? {
            [j in keyof T[i]]:
            T[i][j] extends E ? E2Data[T[i][j]] : never }
        //                      ^^^^^^^^^^^^^^^
        // Type 'T[i][j]' cannot be used to index type 'E2Data'.
        : never
    }
}

Meine derzeitige Problemumgehung besteht darin, eine Typvereinigung zu verwenden. Dadurch kann ich jedoch die gewünschten Daten nicht mithilfe einer Vorlage angeben.

interface B {
    data: (E2Data[E.a] | E2Data[E.b] | E2Data[E.c])[][];
}

Gibt es eine Möglichkeit, die beiden dataoben beschriebenen Methoden der automatischen Vervollständigung für ein 2D-Array zu unterstützen?

let b1: A<[ [E.a], [E.b] ]> = {
    data: [ [{ a: "" }], [{ b: "" }] ]
}

let b2: A = {
    data: [ [<E2Data[E.a]>{ a: "" }], [ <E2Data[E.b]>{ b: "" }] ]
}

Antworten

1 LindaPaiste Sep 26 2020 at 04:22

Ich habe darüber nachgedacht, aber die Lösung ist eigentlich ziemlich einfach, da Sie bereits den größten Teil der Arbeit mit Ihrem Original erledigt haben interface A.

Definieren Sie einen generischen Typ, DataArrayder ein Tupel von E-Werten als generisch verwendet und ein Tupel der entsprechenden E2Data zurückgibt. Sie können neu formatieren, damit DataArraydie Logik enthalten und Averwendet wird. Der Einfachheit halber greifen wir jedoch einfach von auf auf die vorhandene Logik zu A.

type DataArray<T extends E[]> = A<T>['data'];

Anstatt eine verschachtelte Indexsignatur in zu verwenden interface B, können Sie eine einzelne Indexsignatur verwenden und den DataArrayTyp aufrufen , um die inneren Arrays zu erstellen.

interface B<T extends E[][] = E[][]> {
    data: {
        [i in keyof T]: T[i] extends E[] ? DataArray<T[i]> : never
    }
}

Jetzt erstellen Sie einen beliebigen 2D-Datentyp, indem Sie E mischen und abgleichen.

let b: B<[[E.a], [E.b, E.c]]> = {
    data: [ [{ a: "" }], [{ b: "" }, {c: ""}] ]
}

Bearbeiten Ich weiß, dass Sie nicht danach gefragt haben, aber hier erfahren Sie, wie Sie mit tiefer verschachtelten Werten umgehen. (Mit einigen Anleitungen aus dieser Antwort zu rekursiven Arrays).

interface Nested<T> extends Array<Nested<T> | T> {
}

type NestedData<T extends Nested<E>> = { 
    [i in keyof T]: T[i] extends E ? E2Data[T[i]] : T[i] extends Nested<E> ? NestedData<T[i]> : never
}

interface C<T extends Nested<E>> {
    data: NestedData<T>
}

let c: C<[E.a, [E.b, [E.c]]]> = {
    data: [ { a: "" }, [{ b: "" }, [{c: ""}] ]]
}