如何将数据组表示为通用 Typescript 接口
Posted
技术标签:
【中文标题】如何将数据组表示为通用 Typescript 接口【英文标题】:How to represent group of data as a generic Typescript interface 【发布时间】:2020-07-07 12:54:45 【问题描述】:简而言之:
如何使用 Typescript 类型的系统声明一个以类型参数为参数的泛型接口
一个组的类型G
(属于一个组的所有属性)
组中某个项目的类型I
(一个项目的所有属性,其中一个组有几个)
G
中属性的类型 N
,其值为 G[N]
组的项目数组 I[]
并让生成的接口类型是一个组 G,其属性 N 被键入到一个项目 I 的数组中,同时在生成的类型中还包括 G 的所有其他属性。
到目前为止我所尝试的
修订 #2(成功)
在阅读了一些关于计算索引类型(正确的名称?)及其局限性之后,我想出了这种方法:
type GroupOfItems<Group, Item, GroupOn extends keyof Group> =
[n in GroupOn]: Item[];
type A = a: any, children: B[];
type B = b: number;
let obj: GroupOfItems<A, B, 'children'> & Omit<A, 'children'>;
我使用联合类型来组合通用“组”接口和“其他”属性,从而得到我想要的结构的完全类型检查对象。查看this playground
修订 #1
第一个答案显示我有语法错误。它提供了一个具有 3 个类型参数的有效接口。但是,生成的类型缺少项目数组之外的任何组属性其他。
我的下一次尝试,似乎是无效的 Typescript(见 playground):
type GroupOfItems<Group, Item, GroupProp extends keyof Group> =
[OtherProp in keyof Omit<Group, GroupProp>]: Group[OtherProp];
[n in GroupProp]: Item[];
我天真的第一次尝试:
interface GroupOfItems<GroupType, ItemType, NameOfItemsProp extends keyof G>
[key: NameOfItemsProp]: ItemType[];
但是,正如您在 this TS Playground 中看到的那样,Typescript 编译器对此并不满意,我想不出办法。
这个结构的具体用法
假设有以下类型
interface Movie // An example Item
id: number,
title: string,
releaseYear: number,
// ... more movie properties
interface MovieList // An example Group
id: number,
description: string,
// ... more list properties
movies: Movie[], // This property holds the group of items
已定义,我想创建一个泛型类型GroupOfItems<MovieList, Movie, 'movies'>
,它应映射到类似的结构
id: 0,
description: 'A collection of funny movies'
totalRuntimeInSecs: 9815,
// ... all other movielist properties
movies: [
id: 0,
title: 'Blazing Saddles',
releaseYear: 1974,
// ...
]
但是,我还希望具体类型 GroupOfItems<Playlist, Song, 'songs'>
(假设定义了 Playlist
和 Song
)兼容
playlistId: 0,
playlistName: string,
playlistDescription: string,
//... more playlist props
songs: Song[] // This property holds the item array
提问的动机
我正在尝试实现一个通用数据源类,该类可以对其数据进行分组并切换项目组的可见性。虽然输入数据的结构是不变的,但保存组项目的属性名称在现有接口中可能会有所不同。
【问题讨论】:
【参考方案1】:这就是你要找的吗?
interface Movie // An example Item
id: number,
title: string,
releaseYear: number,
// ... more movie properties
interface MovieList // An example group
id: number,
description: string,
// ... more list properties
movies: Movie[], // This property holds the item array
type GroupOfItems<Group, Item, GroupOn extends keyof Group> =
[key in GroupOn]: Item[];
const obj: GroupOfItems<MovieList, Movie, 'movies'> =
id: 0,
description: 'A collection of funny movies',
movies: [
id: 0,
title: 'Blazing Saddles',
releaseYear: 1974,
]
;
【讨论】:
基本上是的。我看到这修复了我的索引类型 ([key in GroupOn]
) 的语法,经过漫长的一天我没有抓住。但是,这还有一个问题,即 GroupOfItems<...>
接口之外的 MovieLiest 属性(例如 id: number
)在结果类型上被标记为不存在。我已将您的答案复制到操场上并正在尝试修复它,但欢迎您提出任何进一步的想法。
在我看来,我自己解决了剩下的问题(请参阅编辑后的答案),但是您帮助我突破了最初阻止我的问题。谢谢!我赞成ofc,但不确定是接受您的回答还是发布我的“最终”解决方案作为答案...
@MaxAxeHax 经过一番查看,我感到困惑的是,obj 可以简单地用const obj: MovieList
表示。这种泛型类型到底有什么意义?
请参阅我的问题中以“但是,我也想要具体类型 GroupOfItems<Playlist, Song, 'songs'>
.. ”开头的部分。基本上,我想要一个泛型类型,其中包含项目的属性的 name 是类型参数。我想用它来构建一个通用数据源类,它可以处理“分组”数据,而不管组中包含“项目”的属性的名称如何。
@MaxAxeHax 即使在您提供的示例中,您仍然可以创建一个“播放列表”对象,您可以声明const obj: Playlist
,所以这似乎没有必要以上是关于如何将数据组表示为通用 Typescript 接口的主要内容,如果未能解决你的问题,请参考以下文章