【问题标题】:Express.js - cast interface by body inside request objectExpress.js - 通过请求对象内的主体转换接口
【发布时间】:2025-12-12 20:00:01
【问题描述】:

我写了一个与 ASP.NET MVC 结构类似的基础,并在我的 github 上发布 (express-mvc)。一切都很好,但我不能在request 对象内投射body 以适应任何界面。

这正是我想要的:

export default interface Post {
    title: string
    body: string
}
import Post from '@models/user'

router.post("/post", (req: Request, res: Response) => {
    const Model: Post = req.body
})

但在运行时没有工作接口,因为 JavaScript 上没有接口。当我使用它时,将在 JavaScript 中输出

const Model;
Model = req.body

这样的话,它会将接口的非成员属性分配给我的变量。

简而言之,我想知道Model 变量中的接口属性。

我正在使用解构来临时解决这个项目中的问题

import Post from '@models/user'

router.post("/post", (req: Request, res: Response) => {
    const { title, body }: Post = req.body

    const Model: Post = <Post>{
        title,
        body
    };
})

你有什么建议


用类解决问题

function castByBody<Model>(source: {}, modelProperties: any[]): Partial<Model> {
    const post: Partial<Model> = {};
    for (const key of modelProperties) {
        post[key] = source[key];
    }
    return post;
}

class User {
  public first_name: string = null
  public username: string = null
  public email: string = null
  public password: string = null
  public age: number = null
}

let user = new User() // create new instance
let userProperties: any[] = Object.getOwnPropertyNames(user) // get all properties from instance

const reqBody = { 
  first_name: "afulsamet",
  username: "aful",
  email: "afulsamet@gmail.com",
  password: "333222233",
  title: "member", 
  age: 18 
} // user instance does not have title property

const model: Partial<User> = castByBody<User>(reqBody, userProperties);

console.log(model)

输出将与控制台中的一样

{
  age: 18
  email: "afulsamet@gmail.com"
  first_name: "afulsamet"
  password: "333222233"
  username: "aful"
}

TypeScript playground

【问题讨论】:

  • 请不要boldface第三 你的问题。它使 it 很难 阅读。

标签: javascript node.js typescript express


【解决方案1】:

这样的话,它会将接口的非成员属性分配给我的变量。

如果你不想这样,那么你就不能使用强制转换(它不是真正的强制转换,在 TypeScript 中它被称为 type assertion)。您必须创建一个新对象。

你可以像你一样使用解构来做到这一点,尽管你可以通过更快地做它来让它感觉不那么笨重(在参数列表中),而且你不需要对其进行类型断言,TypeScript 会看到您分配的内容是可以接受的:

router.post("/post", ({body: { title, body }}, res: Response) => {
    const Model: Post = {
        title,
        body
    };
})

根据类型信息自动执行此操作(无需键入 titlebody)将需要 TypeScript 提供运行时类型信息,即 currently outside TypeScript's goals(尽管这是一个普遍要求的功能)。

当然,它不必是解构的。你可以有一个属性名称数组(TypeScript 不会给你的东西),可能在类型旁边定义,并使用它来代替:

const PostKeys = ["title", "body"];

然后

router.post("/post", ({body}, res: Response) => {
    const Model: {} as Post;
    for (const key of PostKeys) {
        Model[key] = body[key];
    }
})

甚至可以让它成为一个可重用的函数

function createPost(source: any): Post {
    const post: {} as Post;
    for (const key of PostKeys) {
        post[key] = source[key];
    }
    return post;
}

然后

router.post("/post", ({body}, res: Response) => {
    const Model: createPost(body);
})

【讨论】:

  • 解构,几个属性的接口不会有问题。如果我们有太多的属性无法一一编写怎么办?这一点都不好。
  • @AbdulsametKurt - 据我所知,您基本上有两种选择:req.body 上的类型断言(知道它将具有其类型未描述的属性),或列出那些以运行时可访问的方式(可能只是在某个数组中的某个位置)并循环遍历该属性列表。如果我想在 TypeScript 中看到一个运行时类型特性,它会类似于 const MyTypeKeys = keyof MyType; 作为运行时构造。但 AFAIK 它不存在。
  • 是的,我明白了。我已经对这个问题进行了很多研究,并且我通常听说“这仍然超出了我们的设计目标”,但我确信有办法实现这一目标。你还有什么建议吗?
  • 当我查看新的解决方案时,它似乎可以工作。谢谢
  • @AbdulsametKurt - 我很确定目前没有,但在我之前的评论之前,我联系了一个比我更了解 TS 的人征求第二意见。 :-) 他可能会在接下来的几个小时内发表评论(或回答)。
最近更新 更多