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

问题描述 投票:0回答:1

我需要一种类型对象的方法,其中键是特定类型的“事件”字段的值,该值是采用 same 类型的数据子类型的对象的回调数组。

我尝试过使用映射类型,但我是打字稿的初学者,并且真的很挣扎。

// 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 => {}],
};

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

typescript typescript-generics generic-type-argument union-types
1个回答
14
投票

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

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

游乐场


关于

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

© www.soinside.com 2019 - 2024. All rights reserved.