【问题标题】:Typescript, transform an object into another type while preserve its key in the output type打字稿,将对象转换为另一种类型,同时在输出类型中保留其键
【发布时间】:2021-08-23 02:35:22
【问题描述】:

假设我写了这样的代码

type ResourceDecorator = (input: UserResourceDefinition) => DecoratedResourceDefinition
const decorate: ResourceDecorator = ...

const resources = decorate({
   Book1: {
      resourceName: 'my-book',
      resourceType: 'book'
   },
   Pencil1: {
      resourceName: 'my-pencil',
      resourceType: 'pencil'
   } 
})

我想编写函数decorate(...),这样输入类型(Book1Pencil1)中的所有第一级键都保留在输出类型中。换句话说,我想像这样使用输出resources

// somewhere else

console.log(resources.Book1.resourceName)

// the decorate() function will add some programmatically 
// defined properties in addition to the user definition.
console.log(resources.Book1.exampleDecorationProperty) 

我已经尝试过像这样的可索引对象语法,但它不起作用。

export interface UserResourceDefinition {
    [key: string]: {
        resourceName: string,
        resourceType: string,
    }
}

export interface DecoratedResourceDefinition {
    [key: string]: {
        resourceName: string,
        resourceType: string,
        exampleDecorationProperty: string
    }
}

type ResourceDecorator = (input: UserResourceDefinition) => DecoratedResourceDefinition
const decorate: ResourceDecorator = (input) => {
   return Object.entries(definition).map(([resourceKey, userDef]) => ({
        resourceName: userDef.resourceName,
        resourceType: userDef.resourceType,
        resourceKey: resourceKey,
        exampleDecorationProperty: someFunction(userDef)
   })).reduce((accumObj, decoratedDef) => ({ ...accumObj, [decoratedDef.resourceKey]: decoratedDef }), {});
}

它不起作用,因为输出的类型resources 不知道它具有属性Book1Pencil1

// somewhere else

// The auto completion cannot infer 'resources.Book1'
console.log(resources.Book1.resourceName)

// The compiler does not complain about non-existing property 'Foo'
console.log(resources.Foo.resourceName)

Typescript 可以做到这一点吗?

【问题讨论】:

  • 你可以使用泛型定义你想要的类型` T & {[key in keyof T]: {exampleDecorationProperty:string}}, but I'm not sure there is an elegant way of convincing TS that the result of your function conforms to that type. Idk if you're okay with return res as T & {[key in keyof T]: {exampleDecorationProperty:string}}`
  • @NadiaCibrikova 您的解决方案很有希望,但一个新问题是我不知道如何在函数中编写实现以满足类型定义。似乎Object.entries().reduce() 删除了有关输入类型的所有信息。你有什么想法让我试试看。谢谢。
  • 那就是问题所在,我觉得TS没有办法判断修饰后的类型是否正确。因此,如果您确信自己做对了,您可以返回结果,说明它是您需要的类型(使用 as 关键字)。这意味着装饰器函数之外的代码将受到类型检查的保护,但函数本身将易受攻击。

标签: javascript typescript ecmascript-6 types


【解决方案1】:

所以,我的建议是

const decorate= <T extends {}>(input:T) : T & {[key in keyof T]: {exampleDecorationProperty:string}} =>
{
    let decorated = {...input}
    const keys = Object.keys(input) as Array<keyof T>
    keys.forEach(k => decorated[k]={...decorated[k], exampleDecorationProperty:'whatever'})
    return decorated as T & {[key in keyof T]: {exampleDecorationProperty:string}};
}

如果它不适合你,我可以删除这个答案,这样你就有更好的机会从别人那里获得帮助:)

【讨论】:

    猜你喜欢
    • 2021-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 2021-11-19
    • 2020-08-15
    • 2021-01-03
    相关资源
    最近更新 更多