typescript 将数组转换为指定类型的对象

4ktjp1zp  于 2023-01-10  发布在  TypeScript
关注(0)|答案(4)|浏览(467)

我试图把一个类数组转换成一个对象,把它的类名作为对象键。
我有一个数组类:

const renderers = [
    Battery,
    Kind
]

我想转换成这样的对象:

{
  Battery: Battery,
  Kind: Kind
}

为了达到这个目的,我使用reduce来获取Object:

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,然后在reducer中我可以这样做:

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不知何故?

fslejnso

fslejnso1#

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);

也许这就是你想要的。

sg24os4d

sg24os4d2#

数组是否固定为2?如果是,则可能执行以下操作:

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
h5qlskok

h5qlskok3#

遗憾的是,由于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"
zi8p0yeb

zi8p0yeb4#

恐怕你想要的是不可能把一个数组转换成一个对象类型,把类型的名称作为属性名称,至少不需要再次手动指定名称(如另一个答案所建议的)。你可以从另一端开始,有一个符合所需形状的对象,然后把它转换成一个数组:

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

然后你可以用Object.value把它转换成一个数组(或者用多边形填充)

const renderers = Object.values(renderersObject);

相关问题