Typescript 通用消息工厂

Posted

技术标签:

【中文标题】Typescript 通用消息工厂【英文标题】:Typescript Generic message factory 【发布时间】:2021-09-30 20:38:54 【问题描述】:

我的应用程序中有一个用例,我希望能够使用简单的 TypeScript 泛型指定消息结构,并附带一个简单的消息工厂。我想出了以下几点:

export type Message<
  T extends string,
  P extends Record<string, any> = Record<string, never>
> = 
  type: T;
  payload: P;
;

export const msg = <M extends Message<string, Record<string, any>>>(
  type: M['type'],
  payload: M['payload']
): M => ( type, payload );

type UserRegistered = Message<'UserRegistered',  name: string >;

const message = <UserRegistered>msg('UserRegistered',  name: 'Test' );

但我在 ): M =&gt; ( type, payload ); 行上遇到 TS2322 错误:

错误 TS2322: 类型 ' type: M["type"];有效载荷:M[“有效载荷”]; ' 不可分配给类型 'M'。 ' 类型:M[“类型”];有效载荷:M[“有效载荷”]; ' 可分配给“M”类型的约束,但“M”可以用约束“Message>”的不同子类型来实例化。

我正在努力弄清楚这可能是一种类型安全风险,以及为什么它不能转换,因为我本质上是在解构和重构相同的类型。

(如果您质疑这个瘦工厂层的实用性,我指望它通过大量预构建的命名工厂函数帮助可维护性)

【问题讨论】:

【参考方案1】:

根据经验,请尝试从下到上推断函数参数。 我的意思是,不要试图推断 &lt;M extends Message&lt;string, Record&lt;string, any&gt;&gt; 整个 M 泛型类型。首先推断出这个类型的每个嵌套属性,然后就可以全部放在一起了。

至少在您的情况下,没有必要使用显式返回类型。请记住,TS 具有结构类型系统,而不是名义上的。

在调用函数时避免使用显式泛型。 99% 是没有必要的。 TS 应该为你推断所有类型

export type Message<
    T extends string,
    P extends Record<string, any> = Record<string, never>
    > = 
        type: T;
        payload: P;
    ;

export const msg = <
    Type extends string,
    Payload extends Record<string, any> = Record<string, never>
>(
    type: Type,
    payload: Payload
): Message<Type, Payload> => ( type, payload )

type UserRegistered = Message<'UserRegistered',  name: string >;

const message = msg('UserRegistered',  name: 'Test' )

/**
 * UserRegistered is redundant here
 */
const message_: UserRegistered = msg('UserRegistered',  name: 'Test' )

Playground

Here,在我的文章中你可以找到更多关于推断函数参数的信息

【讨论】:

感谢您的广泛而彻底的回复!这个周末我需要再读几遍(已经很晚了),但这似乎确实起到了作用。我使用显式泛型来要求传递预期的有效负载形状,所以我不能调用msg('UserRegistered', wrong: 'payload' )

以上是关于Typescript 通用消息工厂的主要内容,如果未能解决你的问题,请参考以下文章

30分钟学会TypeScript

VScode搭建TypeScript开发环境

TypeScript基础教程

TypeScript基础教程

TypeScript基础教程

typescript入门第一课