【问题标题】:Create Reusable Functions with Typescript使用 Typescript 创建可重用函数
【发布时间】:2021-02-14 10:49:08
【问题描述】:

我有一个返回其他函数的函数,如下所示:

export const makeAudienceDb = () => {
  async function insert({ ...params }: AudienceAttributes) {
    const audience = await AudienceModel.create({ ...params })
    const audienceToJson = audience.toJSON()
    return audienceToJson
  }

  async function findById({ id }: { id: number }) {
    const user = await AudienceModel.findByPk(id)
    return user?.toJSON()
  }

  async function remove({ id }: { id: number }) {
    return AudienceModel.destroy({ where: { id } })
  }

  async function update({
    id,
    ...changes
  }: { id: number } & AudienceAttributes) {
    const updated = await AudienceModel.update(
      { ...changes },
      { where: { id } }
    )
    return updated
  }

  return Object.freeze({
    insert,
    findById,
    remove,
    update,
  })
}

我有其他模型,例如UserModelPostModel,它们具有与makeAudienceDb 相同的数据库操作。例如

export const makeUsersDb = ({ hashPassword, createToken }: DBDeps) => {
  async function insert({ ...params }: User) {
    if (params.password) {
      params.password = await hashPassword(params.password)
    }
    const newUser = await UserModel.create({ ...params })
    const returnedUser = newUser.toJSON()

    const { id } = newUser
    const { name, username, email, password } = returnedUser as User
    const payload = {
      id,
      email,
    }
    const token = createToken(payload)
    const user = { id, name, username, password, email }
    return { user, token }
  }

  async function findById({ id }: { id: number }) {
    const user = await UserModel.findByPk(id)
    return user?.toJSON()
  }

  async function findByEmail({ email }: { email: string }) {
    const user = await UserModel.findOne({ where: { email } })
    return user?.toJSON()
  }

  async function remove({ id }: { id: number }) {
    return UserModel.destroy({ where: { id } })
  }

  async function update({ id, ...changes }: { id: number } & User) {
    const updated = await UserModel.update({ ...changes }, { where: { id } })
    return updated
  }

  return Object.freeze({
    insert,
    findByEmail,
    findById,
    remove,
    update,
  })
}

这会导致不同级别的代码重复。我想知道如何创建一个可以重用于其他数据库操作的主要功能。因此,除了我拥有makeAudienceDbmakeUsersDb,我可以只拥有一个主要功能,例如majorDatabaseOps,其中makeAudienceDbmakeUsersDb 可以继承自。我知道这对于 ES6 类和Interfaces 是可能的,但我只是想知道如何以一种功能性的方式实现它。欢迎任何贡献。非常感谢!

【问题讨论】:

    标签: javascript node.js typescript express


    【解决方案1】:

    看起来您正在尝试将快捷方式绑定到特定的 sequelize 方法。可以使用 typescript 泛型来实现共享功能。覆盖特定行为,例如对 User 的密码进行哈希处理,这会使这变得更加复杂。

    我的第一直觉是使用基于类的方法。但是您可以通过从基础中复制所有方法然后覆盖或添加特定方法来使用函数来实现这一点,如下所示:

    const userDb = Object.freeze({
      ...makeDb(UserModel), 
      findByEmail: async ({email}: {email: string}) => {
      }
    })
    

    我们想要创建一个将模型作为参数的函数。它将使用 typescript 泛型来描述与该模型关联的类型。调用函数时会从model 变量中推断出泛型。

    续集Model 有两个通用值:TModelAttributesTCreationAttributes,这是可选的,默认为TModelAttributes。我们还想要求我们所有的模型属性都必须包含{id: number}

    您可能会添加额外的类型以获得对toJSON 的更好支持。 sequelize 包只是将Model.toJSON 的返回类型声明为object,这是模糊且无益的。

    我们的一般函数如下所示:

    import {Model, ModelCtor} from "sequelize";
    
    export const makeDb = <TModelAttributes extends {id: number} = any, TCreationAttributes extends {} = TModelAttributes>(
        model: ModelCtor<Model<TModelAttributes, TCreationAttributes>>
      ) => {
      async function insert({ ...params }: TCreationAttributes) {
        const created = await model.create({ ...params });
        return created.toJSON();
      }
    
      async function findById({ id }: { id: number }) {
        const found = await model.findByPk(id)
        return found?.toJSON()
      }
    
      async function remove({ id }: { id: number }) {
        return model.destroy({ where: { id } })
      }
    
      async function update({ ...changes }: { id: number } & Partial<TModelAttributes>) {
        const { id } = changes; // I get a TS error when destructuring this in the args
        const updated = await model.update(
          { ...changes },
          { where: { id } }
        );
        return updated;
      }
    
      return Object.freeze({
        insert,
        findById,
        remove,
        update,
      });
    }
    

    对于观众,您只需调用:

    const audienceDb = makeDb(AudienceModel);
    

    或者,如果您愿意,也可以将其定义为函数:

    const makeAudienceDb = () => makeDb(AudienceModel);
    

    对于用户数据库,我们需要覆盖insert,添加findByEmail,并采用附加参数hashPasswordcreateToken

    这并不优雅,但应该可以。我不喜欢用户insert 的返回类型与一般makeDbinsert 返回值不兼容。

    export const makeUsersDb = ({ hashPassword, createToken }: DBDeps) => {
    
      // declaring this up top so that you could call methods on it in your overrides
      const db = makeDb(UserModel);
    
      async function insert({ ...params }: User) {
        if (params.password) {
          params.password = await hashPassword(params.password)
        }
        const newUser = await UserModel.create({ ...params })
        const returnedUser = newUser.toJSON()
    
        const { id } = newUser
        const { name, username, email, password } = returnedUser as User
        const payload = {
          id,
          email,
        }
        const token = createToken(payload)
        const user = { id, name, username, password, email }
        return { user, token }
      }
    
      async function findByEmail({ email }: { email: string }) {
        const user = await UserModel.findOne({ where: { email } })
        return user?.toJSON()
      }
    
      return Object.freeze({
        ...db,
        insert,
        findByEmail
      })
    }
    

    【讨论】:

      【解决方案2】:

      如何在majorDatabaseOps 函数之外定义常用函数?会让您在不同的地方重复使用它们。

      //defined outside for reusability
      function findById(model, id) {
        return model.findByPk(id)
      }
      
      const majorDatabaseOps = model => {
        
        function removeById(model, id) {
          return model.remove(id);
        }
      
        return Object.freeze({
          removeById: id => removeById(model, id),
          findById: id => findById(model, id),
        })
      }
      
      //mocking models for demonstration
      const UserModel = {
        modelName: "UserModel",
        findByPk: function(id) {
          return console.log(id + " was found in " + this.modelName)
        },
        remove: function(id) {
          return console.log(id + " was removed from " + this.modelName)
        }
      }
      
      const PostModel = {
        modelName: "PostModel",
        findByPk: function(id) {
          return console.log(id + " was found in " + this.modelName)
        },
        remove: function(id) {
          return console.log(id + " was removed from " + this.modelName)
        }
      }
      
      
      const userDbOps = majorDatabaseOps(UserModel);
      userDbOps.findById(1);
      userDbOps.removeById(1);
      
      const postDbOps = majorDatabaseOps(PostModel);
      postDbOps.findById(2);
      postDbOps.removeById(2);

      【讨论】:

        猜你喜欢
        • 2021-06-26
        • 1970-01-01
        • 1970-01-01
        • 2019-12-17
        • 1970-01-01
        • 2018-09-12
        • 2023-03-27
        • 2022-01-19
        • 2011-12-30
        相关资源
        最近更新 更多