【问题标题】: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);还有那个打印对象。

标签: arrays node.js json typescript post


【解决方案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']

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 2020-06-06
      • 2018-12-12
      • 2019-04-07
      • 2022-06-17
      • 2019-06-01
      • 2021-08-19
      • 2020-12-27
      相关资源
      最近更新 更多