打字稿二维可变参数模板
Typescript 2D variadic template
我目前有一个接口使用一些其他接口类型的数组的数据成员,这可以通过枚举来确定:
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
知道每个元素的类型(所以实际上是一个元组?)
现在我想为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: "" }] ]
}
这让我开始思考,但解决方案实际上非常简单,因为您已经完成了原始 interface A
.
的大部分工作
定义一个泛型类型 DataArray
,它采用 E 值的元组作为它的泛型,returns 一个相应 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 来创建任何二维数据类型。
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: ""}] ]]
}
我目前有一个接口使用一些其他接口类型的数组的数据成员,这可以通过枚举来确定:
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
知道每个元素的类型(所以实际上是一个元组?)
现在我想为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: "" }] ]
}
这让我开始思考,但解决方案实际上非常简单,因为您已经完成了原始 interface A
.
定义一个泛型类型 DataArray
,它采用 E 值的元组作为它的泛型,returns 一个相应 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 来创建任何二维数据类型。
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: ""}] ]]
}