【问题标题】:Typescript convert array to object with specified type打字稿将数组转换为具有指定类型的对象
【发布时间】:2021-09-15 11:22:56
【问题描述】:

我正在尝试将类数组转换为以类名作为对象键的对象。所以;

我有一个类数组:

const renderers = [
    Battery,
    Kind
]

我想转换成如下对象:

{
  Battery: Battery,
  Kind: Kind
}

为此,我使用reduce 来获取对象:

const convertedObject = renderers.reduce((acc, renderer) => {
    acc[renderer.name] = renderer;
    return acc;
}, {});

所以convertedObject 现在的类型为{}。我希望这个类型是{ Battery: typeof Battery, Kind: typeof Kind}(这样我就可以使用keyof typeof 来创建一个带有字符串值的类型。)

我知道如何通过type RendererType = typeof renderers[0] 获取数组的类型,这让我得到typeof Battery | typeof Kind。然后在减速器中我可以这样做:

renderers.reduce<{[x: string]: RendererType }>((acc, renderer) => {
    acc[renderer.name] = renderer;
    return acc;
}, {});

所以现在,convertedObject 的类型为 { [x: string]: typeof Battery | typeof Kind }ok。但我宁愿它是我之前描述的对象。

我试过了

renderers.reduce<{[K in keyof RendererType]: RendererType }>((acc, renderer) => {
    acc[renderer.name] = renderer;
    return acc;
}, {});

然后我得到{ prototype: typeof Battery | typeof Kind }

有没有办法通过 Typescript 获得我需要的类型?

【问题讨论】:

  • 你们有没有找到解决办法?

标签: typescript


【解决方案1】:

数组是否固定为两个?如果是,那么可能可以执行以下操作:

interface R { battery?: B; kind?: K; } // set fields as optional

renderers.reduce((acc, renderer) => {
  acc[renderer.name] = renderer;
  return acc;
}, {} as R); // strong type it manually 

【讨论】:

  • 很遗憾没有,可以根据需要添加更多内容。
【解决方案2】:
function b<K extends string>(...keys: K[]) : { [T in K]: string }
{
    const init = {} as { [T in K]: string }
    return keys.reduce((a, v) => ({ ...a, [v]: 'label'}), init);
}
var l = b('key1', 'key2');
console.log(l.key1, l.key2);

也许这就是你想要的。

【讨论】:

    【解决方案3】:

    遗憾的是,由于 TypeScript 不将 name 属性作为对象原型中的字符串文字保存,因此目前无法对数组进行此操作。

    class Ball {}
    
    const className = Ball.name; // resolves to just `string`, not `"Ball"`
    console.log(className); // "Ball"
    

    如果我处于你的位置,我会采用以下两种解决方案。

    #1 - 接受渲染器作为对象而不是数组。

    不过,如果渲染器类的顺序无关紧要,我只建议您使用此解决方案。 JavaScript 键值简写可以很容易地满足您的需求。

    class Ball {}
    class Person {}
    
    const paramForFunc = { Ball, Person };
    type RendererNames = keyof typeof paramForFunc; // "Ball" | "Person"
    

    #2 - 要求渲染器实现一个带有名称属性的接口

    interface RendererConstructor {
        readonly alias: string;
    }
    
    class Foo {
        static alias = "Foo" as const;
    }
    
    class Bar {
        static alias = "Bar" as const;
    }
    
    function assertRenderers<T extends readonly RendererConstructor[]>(renderers: T): T[number]["alias"] {
        throw Error("unimplemented");
    }
    
    const foo = assertRenderers([Foo, Bar] as const); // "Foo" | "Bar"
    

    【讨论】:

      【解决方案4】:

      恐怕您无法将数组转换为具有类型名称作为属性名称的对象类型,至少在没有再次手动指定名称的情况下(如另一个答案中所建议)。您可以从另一端开始,拥有一个符合所需形状的对象并将其转换为数组:

      const renderersObject = {
          Battery, // This is the same as Battery: Battery, so we don't have o specify it twice
          Kind
      };
      

      然后您可以使用 Object.value(或它的 polyfill)将其转换为数组

      const renderers = Object.values(renderersObject);
      

      【讨论】:

      • 老实说,这是我第一次去的地方,但我们使用的是 Angular,它的编译器不喜欢它的模块中的函数和其他类似的令人沮丧的东西。
      猜你喜欢
      • 2020-12-15
      • 2021-04-13
      • 1970-01-01
      • 1970-01-01
      • 2021-02-24
      • 2022-10-14
      • 2016-10-05
      • 2020-08-15
      • 1970-01-01
      相关资源
      最近更新 更多