打字稿:将帖子请求正文转换为地图
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有几件事情可能值得澄清,它们使解决这个问题的解决方案变得模糊不清。
第一件事是你的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']
【讨论】:
以上是关于打字稿:将帖子请求正文转换为地图的主要内容,如果未能解决你的问题,请参考以下文章