เทมเพลตตัวแปร 2D typescript

Sep 25 2020

ขณะนี้ฉันมีอินเทอร์เฟซโดยใช้สมาชิกข้อมูลของอาร์เรย์ของประเภทอินเทอร์เฟซอื่น ๆ ซึ่งสามารถกำหนดได้โดย enum:

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: "" } ]
}

ตามความเข้าใจของฉันตอนนี้ฉันสามารถใช้a1.dataและa2.dataเป็นอาร์เรย์ได้ แต่a1.dataรู้ประเภทของแต่ละองค์ประกอบ (จริง ๆ แล้วทูเปิล?)

ตอนนี้ฉันต้องการใช้อาร์เรย์ 2 มิติสำหรับinterface Aข้อมูล

ฉันคิดว่าด้านล่างจะทำงาน แต่มันจะไม่ให้ฉันไปดัชนีการใช้E2DataT[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
    }
}

วิธีแก้ปัญหาปัจจุบันของฉันคือการใช้ยูเนี่ยนประเภท แต่สิ่งนี้จะไม่ให้ฉันระบุข้อมูลที่ฉันต้องการโดยใช้เทมเพลต

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

มีวิธีรองรับ 2 วิธีในการเติมข้อความอัตโนมัติdataตามที่อธิบายไว้ข้างต้นสำหรับอาร์เรย์ 2 มิติหรือไม่

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

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

คำตอบ

1 LindaPaiste Sep 26 2020 at 04:22

สิ่งนี้ทำให้ฉันต้องคิดบ้าง แต่วิธีแก้ปัญหาค่อนข้างตรงไปตรงมาเพราะคุณได้ทำส่วนใหญ่กับต้นฉบับของคุณinterface Aแล้ว

กำหนดประเภททั่วไปDataArrayซึ่งรับค่าทูเปิลของ E เป็นค่าทั่วไปและส่งคืนทูเพิลของ E2Data ที่เกี่ยวข้อง คุณสามารถฟอร์แมตเพื่อให้DataArrayมีเหตุผลและAใช้มัน Aแต่สำหรับความเรียบง่ายให้เพียงเข้าถึงตรรกะที่มีอยู่จาก

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

แทนที่จะใช้ลายเซ็นดัชนีที่ซ้อนกันinterface Bคุณสามารถใช้ลายเซ็นดัชนีเดียวและเรียกใช้DataArrayประเภทเพื่อสร้างอาร์เรย์ภายใน

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

ตอนนี้คุณสร้างประเภทข้อมูล 2D โดยการผสมและจับคู่ E

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

แก้ไขฉันรู้ว่าคุณไม่ได้ขอสิ่งนี้ แต่นี่คือวิธีจัดการกับค่าที่ซ้อนกันอย่างลึกซึ้งยิ่งขึ้น (ด้วยคำแนะนำจากคำตอบนี้เกี่ยวกับอาร์เรย์แบบเรียกซ้ำ)

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: ""}] ]]
}