如何将联合类型指定为对象键 Typescript

Posted

技术标签:

【中文标题】如何将联合类型指定为对象键 Typescript【英文标题】:How to specify union types as object keys Typescript 【发布时间】:2020-01-07 02:58:54 【问题描述】:

我需要一种方法来键入一个对象,其中键是特定类型的“事件”字段的值,值是一个回调数组,它接受一个 相同的对象 类型的数据子类型。

我尝试过使用映射类型,但我是打字稿的初学者,并且在这方面非常挣扎。

// I have this type structure, where the event is always a string, but the data can be anything (but is constrained by the event)

interface EventTemplate 
  event: string;
  data: any;


export interface CreateEvent extends EventTemplate 
  event: 'create_game';
  data: 
    websocketID: 'string';
  ;


export interface JoinEvent extends EventTemplate 
  event: 'join_game';
  data: 
    gameID: 'string';
  ;


export interface MessageEvent extends EventTemplate 
  event: 'message';
  data: string;


export type WSEvent = CreateEvent | JoinEvent | MessageEvent;

// I want an object like this

type callbacks = 
  [key in WSEvent['event']]: ((data: WSEvent['data']) => void)[];
;

// Except that it forces the data structure to match with the key used. IE using a specific WSEvent rather than a generic one

// Something along the lines of:

type callbacks = 
  [key in (T extends WSEvent)['event']]: ((data: T['data']) => void)[];
;
// ...only valid..

const callbacks: callbacks = 
  // So this should be valid:
  message: [(data: MessageEvent['data']): void => , (data: MessageEvent['data']): void => ],

  // But this should not be valid, as CreateEvent doesn't have the event 'join_game'
  join_game: [(data: CreateEvent['data']): void => ],
;

如果有帮助,我很乐意重组上述任何内容。

【问题讨论】:

你在这里达到了极限。您现在需要生成代码。使用节点代码作为 CLI,根据一些现有代码生成项目所需的接口 @AliHabibzadeh OP 还没有达到极限。 TypeScript 的条件类型和映射类型非常强大。看我的回答;) 【参考方案1】:

我们本质上需要的是一种通过提供事件名称来查找整个事件类型的方法。这可以使用conditional helper type 来完成

type EventByName<E extends WSEvent['event'], T = WSEvent> = T extends event: E ? T : never;

第一个通用参数E 必须是事件名称之一。第二个是我们试图缩小范围的联合类型。它默认为WSEvent,因此无需指定它。然后,条件表达式仅返回联合类型中扩展 event: E 的那些事件(其中 E 是事件名称)。

一旦我们有了辅助类型,就很容易相应地调整现有的回调映射类型:

type Callbacks = 
  [E in WSEvent['event']]: ((data: EventByName<E>['data']) => void)[];
;

Playground


关于callbacks 名称的旁注。建议对类型使用 PascalCase。它更容易与变量区分开来。我在示例中将其更改为 Callbacks

【讨论】:

非常感谢!太棒了!非常清晰和有用的解释。 不客气。考虑将答案标记为已接受,以便其他人清楚您的问题已解决。

以上是关于如何将联合类型指定为对象键 Typescript的主要内容,如果未能解决你的问题,请参考以下文章

从具有不同键的嵌套对象的值创建联合

打字稿中具有联合类型键的松散类型对象

使用对象的键作为联合类型

JS超集对TypeScript的Map对象以及联合类型的深入实战

将对象转换为联合类型

将打字稿接口属性类型转换为联合[重复]