Mẫu đa dạng 2D Typecript

Sep 25 2020

Tôi hiện có một giao diện sử dụng thành viên dữ liệu của một mảng thuộc một số kiểu giao diện khác, có thể được xác định bằng 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: "" } ]
}

Theo hiểu biết của tôi, bây giờ tôi có thể sử dụng a1.datavà a2.datadưới dạng mảng, nhưng a1.databiết loại của từng phần tử (vì vậy thực sự là một bộ tuple?)

Bây giờ tôi muốn sử dụng một mảng 2D cho interface Adữ liệu của.

Tôi nghĩ phần dưới đây sẽ hoạt động, nhưng nó không cho phép tôi lập chỉ mục E2Databằng cách sử dụng 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
    }
}

Cách giải quyết hiện tại của tôi là sử dụng liên hợp kiểu, nhưng điều này sẽ không cho phép tôi chỉ định dữ liệu tôi muốn bằng cách sử dụng mẫu.

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

Có cách nào để hỗ trợ 2 phương pháp tự động hoàn thành datanhư được mô tả ở trên cho mảng 2D không?

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

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

Trả lời

1 LindaPaiste Sep 26 2020 at 04:22

Điều này khiến tôi phải suy nghĩ, nhưng giải pháp thực sự khá đơn giản vì bạn đã thực hiện hầu hết các công việc với bản gốc của mình interface A.

Xác định kiểu chung DataArraylấy một bộ giá trị E làm giá trị chung và trả về một bộ dữ liệu E2Data tương ứng. Bạn có thể định dạng lại để DataArraychứa logic và Asử dụng nó, nhưng để đơn giản, chúng ta chỉ cần truy cập logic hiện có từ đó A.

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

Thay vì sử dụng chữ ký chỉ mục lồng nhau trong interface B, bạn có thể sử dụng một chữ ký chỉ mục duy nhất và gọi DataArraykiểu để tạo các mảng bên trong.

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

Bây giờ bạn tạo bất kỳ kiểu dữ liệu 2D nào bằng cách trộn và kết hợp E.

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

Chỉnh sửa Tôi biết bạn không yêu cầu điều này, nhưng đây là cách xử lý các giá trị được lồng sâu hơn. (Với một số hướng dẫn từ câu trả lời này về mảng đệ quy).

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