I'm trying to convert an array of classes to a object that has it's class name as the object key. So;
I have an array of classes:
const renderers = [
Battery,
Kind
]
And I want to convert to an object like:
{
Battery: Battery,
Kind: Kind
}
To get to that, I use reduce
to get the Object:
const convertedObject = renderers.reduce((acc, renderer) => {
acc[renderer.name] = renderer;
return acc;
}, {});
So convertedObject
now has a type of {}
. I want this type to be { Battery: typeof Battery, Kind: typeof Kind}
(so that I can use keyof typeof
to create a type with the string values.)
I know how to get the type of the array by doing type RendererType = typeof renderers[0]
, which gets me typeof Battery | typeof Kind
. Then in the reducer I can do this:
renderers.reduce<{[x: string]: RendererType }>((acc, renderer) => {
acc[renderer.name] = renderer;
return acc;
}, {});
So now, convertedObject
has a type of { [x: string]: typeof Battery | typeof Kind }
, which is ok. But I rather it be the object I described earlier.
I tried to do
renderers.reduce<{[K in keyof RendererType]: RendererType }>((acc, renderer) => {
acc[renderer.name] = renderer;
return acc;
}, {});
But then I just get { prototype: typeof Battery | typeof Kind }
Is there a way to get the type that I would need with Typescript somehow?
Sadly, this is currently impossible to do with arrays due to the fact that TypeScript does not hold the name
property as a string literal in an object's prototype.
class Ball {}
const className = Ball.name; // resolves to just `string`, not `"Ball"`
console.log(className); // "Ball"
Here are the two solutions that I would go with if I was in your position.
Although, I only advise you go with this solution if the order of your renderer classes does not matter. The JavaScript key-value shorthand would make this easy to incorporate for your needs.
class Ball {}
class Person {}
const paramForFunc = { Ball, Person };
type RendererNames = keyof typeof paramForFunc; // "Ball" | "Person"
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"
Is the array fixed to two? If yes, then probably can do the below:
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
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);
Maybe that's you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With