【问题标题】:Typescript 2D variadic templateTypescript 2D 可变参数模板
【发布时间】:2020-09-25 11:04:44
【问题描述】:

我目前有一个接口使用其他接口类型的数组的数据成员,可以通过枚举确定:

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.dataa2.data 作为数组,但是a1.data 知道每个元素的类型(所以实际上是一个元组?)

现在我想为interface A 的数据使用二维数组。

我认为下面的方法可行,但它不允许我使用 T[i][j] 索引 E2Data

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])[][];
}

有没有办法支持data 上的 2 种自动完成方法,如上文所述的二维数组?

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

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

【问题讨论】:

  • B.data 中的各个数组是否包含不同数据类型的组合,还是按类型分组?例如,如果您只想要一个 E.a 数据数组和一个 E.b 数据数组,那么这很容易。您需要做的就是将接口 A 中的 E2Data[T[i]] 替换为 E2Data[T[i]][]。但我怀疑您需要混合类型,否则您不会发布此内容。
  • 所需的数据类型与解决方法 interface B 中的一样。无论如何,您的回答都涵盖了这一点!

标签: typescript templates generics variadic


【解决方案1】:

这让我思考了一些问题,但解决方案实际上非常简单,因为您已经使用原始 interface A 完成了大部分工作。

定义一个泛型类型DataArray,它将一个 E 值的元组作为其泛型,并返回一个对应 E2Data 的元组。您可以重新格式化,以便 DataArray 包含逻辑并 A 使用它,但为简单起见,我们只访问来自 A 的现有逻辑。

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

您可以使用单个索引签名并调用DataArray 类型来创建内部数组,而不是在interface B 中使用嵌套索引签名。

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

现在您可以通过混合和匹配 E 来创建任何 2D 数据类型。

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

编辑 我知道你没有要求这个,但这里是如何处理更深层嵌套的值。 (在this answer 的一些关于递归数组的指导下)。

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

【讨论】:

    猜你喜欢
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    相关资源
    最近更新 更多