有没有一种简单的方法可以使用带有类型消息的 BroadcastChannel 来代替任何方法?

问题描述 投票:0回答:2
const channel = new BroadcastChannel('foo');

channel.postMessage(<any>);

我知道有一个库(https://github.com/pubkey/broadcast-channel#create-a-typed-channel-in-typescript),但我不想包含任何额外的依赖关系,我只是想让打字稿在编译时检查消息的类型

import { BroadcastChannel } from 'broadcast-channel';
declare type Message = {
  foo: string;
};
const channel: BroadcastChannel<Message> = new BroadcastChannel('foobar');
channel.postMessage({
  foo: 'bar'
});
typescript broadcast channel
2个回答
3
投票

您可以创建一个扩展原生

BroadcastChannel
的自定义类,使
postMessage
方法严格键入:

export class StrictBroadcastChannel<
  MessageType extends Record<string, any>,
> extends BroadcastChannel {
  public postMessage(message: MessageType): void {
    return super.postMessage(message)
  }
}

用途:

type MessageType =
 | { type: 'hello', user: string }
 | { type: 'goodbye' }

const channel = new StrictBroadcastChannel<MessageType>()

channel.postMessage({ type: 'hello' }) // TypeError: missing "user"
channel.postMessage({ type: 'goodbye' }) // OK

您可以从这里开始,按照您喜欢的方式复杂化。我将消息结构嵌入到类本身中,因此所有消息都具有

{ type: string, payload?: unknown }
形状,然后我创建一个消息映射,如下所示:

interface DataMessageMap {
  DATA_START(clientId: string): void
  DATA_CHUNK(chunk: string): void
  DATA_END(): void
}

0
投票

虽然 kettanaito 的答案提供了 postMessage 的类型,但它不提供侦听器端的类型安全(

onmessage
)。此外,如果您只需要编译时类型检查,则类继承会产生额外的开销。

幸运的是,BroadcastChannel 是在 Typescript 的 DOM 类型中输入的。

基于上面的接口定义,我创建了以下泛型类型的 BroadcastChannel 定义:

interface StrictBroadcastChannelEventMap<T> {
  "message": MessageEvent<T>;
  "messageerror": MessageEvent<T>;
}

export interface StrictBroadcastChannel<T> extends EventTarget {
  readonly name: string;
  onmessage: ((this: BroadcastChannel, ev: MessageEvent<T>) => any) | null;
  onmessageerror: ((this: BroadcastChannel, ev: MessageEvent<T>) => any) | null;
  close(): void;
  postMessage(message: T): void;
  addEventListener<K extends keyof StrictBroadcastChannelEventMap<T>>(type: K, listener: (this: BroadcastChannel, ev: StrictBroadcastChannelEventMap<T>[K]) => any, options?: boolean | AddEventListenerOptions): void;
  addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
  removeEventListener<K extends keyof StrictBroadcastChannelEventMap<T>>(type: K, listener: (this: BroadcastChannel, ev: StrictBroadcastChannelEventMap<T>[K]) => any, options?: boolean | EventListenerOptions): void;
  removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}

然后您可以使用以下代码进行编译时检查:

type ControlMessage = {
  type: 'showVideo';
  videoUrl: string;
} | {
  type: 'closeVideo'
}

const bc: StrictBroadcastChannel<ControlMessage> = new BroadcastChannel('my_channel');

bc.onmessage = function(ev) {
  switch (ev.data.type) {  // Automatic code completion when access ev.data
    case 'showVideo':
      console.log('showVideo', ev.data.videoUrl);
      break;
    case 'closeVideo':
      console.log('closeVideo');
      break;
  }
}

bc.postMessage({ type: 'showVideo', videoUrl: 'https://example.com/1.mp4' }); // OK
bc.postMessage({ type: 'closeVideo', extraKey: 1 }); // Error: 'extraKey' does not exist in type

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