首页 > 解决方案 > 如何在 TypeScript 中表达正确类型的消息队列?

问题描述

我正在探索如何在 TypeScript 中编写正确类型的消息队列,其中每个主题都由符号标识并具有自己的消息类型。我正在寻找以下形式的东西:

const names = Symbol('name');
const ages = Symbol('age');
type Name = string;
type Age = number;

type TopicId = Symbol;

interface Messager {
  publish(topic: TopicId, message: any): Promise<boolean>,
  subscribe(topic: TopicId, consumer: (message: any) => Promise<boolean>): void
}

const messager: Messager<NameTopic | AgeTopic> = null;
messager.publish(names, "test-name");
messager.publish(ages, 23);

以便发布和订阅方法的消息类型根据主题的类型进行区分。例如,我希望该messager值是Messager允许在主题上发布字符串和在name主题上发布数字的类型的实例age

这感觉像是分布式条件类型的工作,但我担心正确的语法让我难以理解。

标签: typescript

解决方案


我倾向于定义这样的事情:

type Topic = {
  symbol: Symbol;
  type: unknown
}

interface Messager<T extends Topic> {
  publish<S extends T["symbol"]>(
    topic: S,
    message: Extract<T, { symbol: S }>["type"]
  ): Promise<boolean>,
}

所以 aTopic是一对符号和类型。然后, aMessager可以是泛型的T,类型的联合Topic。您的方法将是通用的S,其中的符号之一T(具体来说,您正在查找"symbol"属性T,即 ,T["symbol"]如果是联合,它将T是联合)。

对于publishtopic参数只是S。然后我们要将message参数约束为相应的"type"属性。这就是那些分布式条件类型的用武之地。在这里,我使用Extract<A, B>了一个实用程序类型,它采用联合类型A并只保留它的可分配给的成员B。因此,如果我们采取Extract<T, {symbol: S}>,我们应该只得到"symbol"属性所在的主题S。然后我们查找它的"type"属性......给我们类型Extract<T, {symbol: S}>["type"]

让我们看看它的实际效果:

const names = Symbol('name');
type NameTopic = {
  symbol: typeof names;
  type: string
}

const ages = Symbol('age');
type AgeTopic = {
  symbol: typeof ages;
  type: number;
}

这里我们有两种Topic类型,NameTopicAgeTopic。其余代码按您的意愿进行:

const messager: Messager<NameTopic | AgeTopic> = null!;
messager.publish(names, "test-name"); // okay
messager.publish(names, 100); // error! 100 is not a string
messager.publish(ages, 23); // okay
messager.publish(ages, "NaN") // error! "NaN" is not a number

const shoesizes = Symbol("shoesizes")
messager.publish(shoesizes, 123); // error, not the right symbol

编译器强制正确的值与正确的符号一起使用。希望有帮助;祝你好运!

链接到代码


推荐阅读