如何将数据组表示为通用 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&lt;MovieList, Movie, 'movies'&gt;,它应映射到类似的结构

 
    id: 0,
    description: 'A collection of funny movies'
    totalRuntimeInSecs: 9815,
    // ... all other movielist properties
    movies: [
        
            id: 0,
            title: 'Blazing Saddles',
            releaseYear: 1974,
            // ... 
        
    ]

但是,我还希望具体类型 GroupOfItems&lt;Playlist, Song, 'songs'&gt; (假设定义了 PlaylistSong)兼容


    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&lt;...&gt; 接口之外的 MovieLiest 属性(例如 id: number)在结果类型上被标记为不存在。我已将您的答案复制到操场上并正在尝试修复它,但欢迎您提出任何进一步的想法。 在我看来,我自己解决了剩下的问题(请参阅编辑后的答案),但是您帮助我突破了最初阻止我的问题。谢谢!我赞成ofc,但不确定是接受您的回答还是发布我的“最终”解决方案作为答案... @MaxAxeHax 经过一番查看,我感到困惑的是,obj 可以简单地用const obj: MovieList 表示。这种泛型类型到底有什么意义? 请参阅我的问题中以“但是,我也想要具体类型 GroupOfItems&lt;Playlist, Song, 'songs'&gt; .. ”开头的部分。基本上,我想要一个泛型类型,其中包含项目的属性的 name 是类型参数。我想用它来构建一个通用数据源类,它可以处理“分组”数据,而不管组中包含“项目”的属性的名称如何。 @MaxAxeHax 即使在您提供的示例中,您仍然可以创建一个“播放列表”对象,您可以声明const obj: Playlist,所以这似乎没有必要

以上是关于如何将数据组表示为通用 Typescript 接口的主要内容,如果未能解决你的问题,请参考以下文章

如何在 TypeScript 3+ 中正确键入通用元组剩余参数?

Typescript - 为非常通用的功能定义接口[重复]

具有通用方法的Typescript接口

在 Python 中实现 Typescript 接口

TypeScript 类型推断 - 函数的通用对象

如何将 json 转换为 typescript 接口?