【问题标题】:Write a function that, given a prop name as argument, returns an object prop correctly编写一个函数,给定一个 prop 名称作为参数,正确返回一个对象 prop
【发布时间】:2023-02-09 19:29:20
【问题描述】:

我有一个函数来编写数据库查询。这是一个过于简单的例子:

// DB Types
type UserData = {
  id: string,
  name: string,
  email: string
}

type UserSettings = {
  id: string,
  language: string,
  country: string
}

type Tables = {
  user: 'user'
  userSettings: 'userSettings'
}


const dbQueries = {
  user: {
    create: (args: {data: UserData}) => {/* ...queryDB */ },
    read: (userId: string) => {/* ...queryDB */ }
    //... other CRUD queries
  },
  userSettings: {
    create: (args: {data: UserSettings}) => {/* ...queryDB */ },
    read: (userSettingId: string) => {/* ...queryDB */ }
    //... other CRUD queries
  }
}

type AllowedTables = keyof Tables

// Here I want to wrap all the queries to modify some of them
// and return all the queries for a specific entity (e.g. all
// the queries for 'userSettings')
const getModifiedQueriesForEntity = <
  EntityName extends AllowedTables,
>(entityName: EntityName) => {
  const queries = dbQueries[entityName];

  // I want to wrap the original `create` query to do two things:
  // 1. avoid having to wrap the entity into 'data'
  // 2. abstract away the ID generation, so that the caller doesn't have
  //    to worry about it.
  const create = (args: Parameters<typeof queries.create>[0]['data']) => {
    const id: string = getRandomId(); // returns a string
    return queries.create({data: {...args, id}});
  }

  // I leave the other queries untouched
  return {
    ...queries,
    create
  }
}

但是在 return queries.create({data: {...args, id}}); 行,我收到以下 ts 错误:

Type '{ id: any; name: string; email: string; } | { id: any; language: string; country: string; }' is not assignable to type 'UserData & UserSettings'.
  Type '{ id: any; name: string; email: string; }' is not assignable to type 'UserData & UserSettings'.
    Type '{ id: any; name: string; email: string; }' is missing the following properties from type 'UserSettings': language, countryts(2322)

并且调用该函数不会返回正确的签名:

const {create: createUser} = getModifiedQueriesForEntity('user');

// here intellisense suggests __all__ properties of User and UserSettings **combined**
createUser({
  country, // from UserSettings
  name // from User
})

我知道打字稿试图只找到适用于 User 和 UserSetting 类型的东西,但我希望它根据 entityName 辨别这两者。

【问题讨论】:

  • getRandomId() 是否返回类型字符串?
  • 是的@BrendanOtherwhyz。我将编辑问题以使其更清楚,谢谢。

标签: typescript


【解决方案1】:

在打字稿中很难在运行时检测类型,因此您必须非常谨慎和明确地使用它们。可以在运行时推断类型,但您需要使用type predicates。这可能是矫枉过正。

主要问题是,如果您从函数返回一个对象,并且它本身包含一个函数,该函数可以根据输入具有不同的参数类型。除了将参数类型组合在一起之外,Typescript 不知道如何处理这个问题。因此,为什么您需要将它们分开。因此,据我所知,在回答标题中的问题时,这是不可能的。

这就是我在上面实现您的代码的方式。

// DB Types
type UserData = {
    id: string,
    name: string,
    email: string
}

type UserSettings = {
    id: string,
    language: string,
    country: string
}

type Tables = {
    user: 'user'
    userSettings: 'userSettings'
}

// create function arguments accept data with both types (UserData and UserSettings)
const dbQueries = {
    user: {
        create: (args: { data: UserData }) => {/* ...queryDB */ },
        read: (userId: string) => {/* ...queryDB */ }
        //... other CRUD queries
    },
    userSettings: {
        create: (args: { data: UserSettings }) => {/* ...queryDB */ },
        read: (userSettingId: string) => {/* ...queryDB */ }
        //... other CRUD queries
    }
}

enum AllowedTables {
    user = 'user',
    userSettings = 'userSettings'
}

function getRandomId(): string {
    return '1';
}

var createUser;
let entityName = AllowedTables.user;
if (entityName == AllowedTables.user) {
    createUser = {
        ...dbQueries.user,
    }
    createUser.create = (args: { data: UserData }) => {
        const id = getRandomId();
        const data = { ...args };
        data.data.id = id;
        return dbQueries.user.create(data)
    }
    createUser.create({
        data: {
            id: 'foo',
            name: 'bar',
            email: 'baz'
        }
    })
} else if (entityName == AllowedTables.userSettings) {
    createUser = {
        ...dbQueries.userSettings
    }
    createUser.create = (args: { data: UserSettings }) => {
        const id = getRandomId();
        const data = { ...args }
        data.data.id = id;
        return dbQueries.userSettings.create(data)
    }
    createUser.create({
        data: {
            id: 'foo',
            language: 'bar',
            country: 'baz'
        }
    })
}

我希望这有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    • 2019-11-25
    • 2021-08-23
    • 2014-03-26
    • 2013-08-23
    • 2014-03-23
    • 1970-01-01
    相关资源
    最近更新 更多