打字稿:将帖子请求正文转换为地图

Posted

技术标签:

【中文标题】打字稿:将帖子请求正文转换为地图【英文标题】:Typescript : convert post request body to map 【发布时间】:2019-09-20 02:10:44 【问题描述】:

我正在用 node js 和 typescript 编写一个 rest api,为了创建用户,我的 api 收到一个 json 帖子:

import Request, Response, Router from "express";
import User from '../../../models/user.model';
import createUser from '../../../factories/user.factory';

export default [
        
            path: "/api/v1/user/create",
            method: "post",
            handler: [
                async (req: Request, res: Response) => 
                    createUser(new User(req.body.user));
                    res.status(200).send(req.body);
                
            ]
        
    ];

例如,我发送:


    "user": 
        "email": "test@gmail.com",
        "password": "12345678",
        "firstName": "Jérémy"
        
    

我想用对象 req.body.user 创建一个对象“用户”:

import Timestamp from './timestamp.model';

export class User 
    id: bigint | undefined;
    email: string | undefined;
    password: string | undefined;
    firstName: string | undefined;
    lastName: string | undefined;
    pseudo: string | undefined;
    birthDate: Timestamp | undefined;
    lastEditDate: Timestamp | undefined;
    creationDate: Timestamp | undefined;
    googleAuthToken: string | undefined;
    language: string | undefined;
    profileAvatarUrl: string | undefined;
    profileBannerUrl: string | undefined;
    primaryLightColor: string | undefined;
    secondaryLightColor: string | undefined;
    primaryDarkColor: string | undefined;
    secondaryDarkColor: string | undefined;

    constructor(array: object) 
        console.log(array);
        // @ts-ignore
        console.log(array.gg);
        // @ts-ignore
        this.id = array.id;
        // @ts-ignore
        this.email = array.email;
        // @ts-ignore
        this.password = array.password;
        // @ts-ignore
        this.firstName = array.firstName;
        // @ts-ignore
        this.lastName = array.lastName;
        // @ts-ignore
        this.pseudo = array.pseudo;
        // @ts-ignore
        this.birthDate = array.birthDate;
        // @ts-ignore
        this.lastEditDate = array.lastEditDate;
        // @ts-ignore
        this.creationDate = array.creationDate;
        // @ts-ignore
        this.googleAuthToken = array.googleAuthToken;
        // @ts-ignore
        this.language = array.language;
        // @ts-ignore
        this.profileAvatarUrl = array.profileAvatarUrl;
        // @ts-ignore
        this.profileBannerUrl = array.profileBannerUrl;
        // @ts-ignore
        this.primaryLightColor = array.primaryLightColor;
        // @ts-ignore
        this.secondaryLightColor = array.secondaryLightColor;
        // @ts-ignore
        this.primaryDarkColor = array.primaryDarkColor;
        // @ts-ignore
        this.secondaryDarkColor = array.secondaryDarkColor;
        // @ts-ignore
    

     toMap() 
        return 
            "id": this.id,
            "email": this.email,
            "firstName": this.firstName,
            "lastName": this.lastName,
            "pseudo": this.pseudo,
            "profileAvatarUrl": this.profileAvatarUrl,
            "birthDate": this.birthDate,
            "lastEditDate": this.lastEditDate,
            "creationDate": this.creationDate,
            "language": this.language,
            "googleAuthToken": this.googleAuthToken,
            "profileBannerUrl": this.profileBannerUrl,
            "primaryLightColor": this.primaryLightColor,
            "secondaryLightColor": this.secondaryLightColor,
            "primaryDarkColor": this.primaryDarkColor,
            "secondaryDarkColor": this.secondaryDarkColor,
        
    


我已经把所有这些 "// @ts-ignore" 因为如果没有,我有这个错误:

src/models/user.model.ts(27,25): 错误 TS2339: 属性 'id' 不 存在于类型“对象”上。 src/models/user.model.ts(28,32):错误 TS2339: “对象”类型上不存在属性“电子邮件”。 src/models/user.model.ts(29,35):错误 TS2339:属性“密码” 在“对象”类型上不存在。 src/models/user.model.ts(30,36): 错误 TS2339:“对象”类型上不存在属性“名字”。 src/models/user.model.ts(31,35):错误 TS2339:属性“lastName” 类型“对象”上不存在。

我的问题是:如何正确地让我的班级用户不必输入所有这些“// @ts-ignore”?

提前致谢。 杰里米。

【问题讨论】:

为什么指定array: object?您肯定希望这里有别的东西吗? 我已经使用 Array 和 Map 进行了测试,但是我无法使用 ts_ignore 访问我的 values 事件。我已将其添加到我的代码中:console.log(typeof req.body.user);还有那个打印对象。 【参考方案1】:

有几件事情可能值得澄清,它们使解决这个问题的解决方案变得模糊不清。

第一件事是你的toMap 方法有点不必要,因为 ES6 类一个template for creating an object,所以用new 关键字创建你的类的实例会给你返回一个对象,而toMap 方法实际上并不返回User 对象,即使它符合类型。如果您想返回User,则需要将您的签名修改为:

toMap(): User  ... 

这会通知编译器您打算返回 User,否则,编译器只会看到该对象并将其推断为具有您定义的属性的具体对象。

此外,您的构造函数具有以下签名:

constructor(array: object)  ... 

首先,调用您的变量array 势必会引起混乱,对于阅读您的代码的人,他们会假设他们正在使用数组,但您将其键入为@987654333 @。

就类型错误而言,你得到它们是因为 TypeScript 的 object 是因为它代表 non-primitive type,实际上根本没有任何属性,所以当你尝试访问一个时,编译器可以'访问它。

使用您当前的代码,您可以将 object 替换为 any,它很可能会编译,但我认为您不会想要这样。

有一个更简洁的方法来完成你所追求的,你可以通过在构造函数中设置它们来定义你的属性及其类型,如下所示:

class User 
  constructor(
    public id?: string, // or private
    public email?: string
  ) 


const user = new User("abc123", "this@example.com")

console.log(user)

# >>> User:  "id": "abc123", "email": "this@example.com" 

另请注意,您可以使用问号表示输入是可选的。

如果您想接受 json 对象作为输入而不是将所有内容都放入构造函数中,您始终可以在接受 any 作为输入并返回该类实例的类上定义一个静态方法。

例如见fiddle。

【讨论】:

【参考方案2】:

我有一个不同的建议,我觉得打字稿很好,并开始大量使用。您可以将其定义为接口,而不是为您的用户创建一个类。

export interface User  
 email: string, 
 password: string, 
 firstName: string,
 lastName: string, 
 // etc 

然后简单地做:

const user = req.body.user as User; 

只要您将这些仅用于创建没有业务逻辑的域模型对象,键入起来会更快、更清晰。

编辑:

如果您需要坚持上课,请尝试使用 any 输入。

 export class user  
 constructor(userDto: any)  
   // your logic
    
 


 new User(req.body.user); 

【讨论】:

谢谢,我现在有了这个解决方案,但我必须将函数放在我的类用户中,例如:user.toMap();为此,我必须有一堂课: 关于你回复第一部分的类型断言,值得指出的是,这只是告诉编译器输入是一个用户。它不会对输入强制执行任何操作,如果您的有效负载不符合用户类型,您仍然很容易受到运行时错误的影响。换句话说,您仍然需要验证输入实际上是 User 类型。 有没有办法做到这一点,它只选择与接口/类匹配的字段?我喜欢它的简洁性,但不希望它从请求正文中获取其他字段。只匹配类上的属性。 @ontek 有没有办法解决这个问题,如果 req.body 没有确认类型,它会抛出错误? @lordvcs 不是真的。要概念化为什么会这样,您必须考虑任何不是 javascript 的东西都不会进入编译输出。换句话说,接口是用于编译器的,而不是运行时的。至于重用接口进行验证,您需要使用类似github.com/gristlabs/ts-interface-checker 的东西(YMMV 没用过)。这个github.com/joiful-ts/joiful 看起来很有希望,但它使用 TS 生成器来注释一个类并将所有验证放在一个地方。【参考方案3】:

我特别喜欢 Dan 的解决方案,既干净又快速。但是如果他需要 ToMap 函数,你可以考虑使用https://lodash.com/ 是一个非常方便的库,有助于数组、对象映射、深度克隆。

问候

PD:你也可以使用索引器secondaryDarkColor =array.['secondaryDarkColor']

【讨论】:

以上是关于打字稿:将帖子请求正文转换为地图的主要内容,如果未能解决你的问题,请参考以下文章

打字稿:将 HTMLElement 转换为节点

如何将打字稿界面转换为棱镜模型

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

打字稿将联合转换为交集[重复]

将firebase json转换为打字稿数组

将 JSON 转换为数组打字稿