Reputation: 857
I'll try to elaborate a bit more because the title is not very clear.
So, I have a few objects of different types where the only things that is the same in all of them is a property which the name (not the type) is the same in all of them. They are all properties of a main object.
I am going to run a reduce to create a new object with properties where the values are the values of those properties with the same name in those objects but the property names are the property names from the main object.
What I need is that the resulting type from this reduce is an object with those props. But I also need this to be a reusable type.
So let's say I have the following object structure:
type SameFoo = {
a: string;
};
type SameBar = {
b: number;
};
type FooType = {
propertyWithSameName: SameFoo;
};
type BarType = {
propertyWithSameName: SameBar;
};
type Main = {
foo: FooType;
bar: BarType;
};
const main: Main = {
foo: { propertyWithSameName: { a: 'something' } },
bar: { propertyWithSameName: { b: 1 } }
};
Now, I'll run a reduce that will transform this into:
const result = {
foo: { a: 'something' },
bar: { b: 1 }
};
The catch here is that I don't know the input types are or the names of the properties, the only thing I know here is the structure and the name of that property with the same name.
// What I know about the structure:
type WhatIKnowAboutTheObject = {
[propName: string]: {
propertyWithSameName: unknown;
[key: string]: unknown;
}
}
But since I want this to be strongly typed, I need to either have the reduce output the object with the correct type or maybe have a type that transforms it, like let's say:
type Result = TransformType<Main>;
// Resulting type:
// type Result = {
// foo: SameFoo;
// bar: SameBar;
// };
So I can do something like:
type Result = TransformType<Main>;
const result: Result = Object.keys(main).reduce(..., {} as Result);
I hope this is clearer, I know this is a very edge use case.
Thanks in advance for the help.
Upvotes: 2
Views: 2477
Reputation: 328618
So, I think you're looking for something like this:
function transform<T extends Record<keyof T, { propertyWithSameName: unknown }>>(
obj: T
) {
return (Object.keys(obj) as Array<keyof T>).reduce(
(acc, k) => (acc[k] = obj[k].propertyWithSameName, acc),
{} as {
[K in keyof T]: T[K]['propertyWithSameName'];
});
}
const result = transform(main);
// const result: {
// foo: SameFoo;
// bar: SameBar;
// }
Basically the TransformType<>
you want is a mapped type that iterates over all the properties and performs a lookup of the propertyWithSameName
for each one. Specifically, if the object is of type T
, you want {[K in keyof T]: T[K]['propertyWithSameName'];})
. Note that you need to constrain T
to a type like Record<keyof T, {propertyWithSameName: unknown}>
, which is similar to your WhatIKnowAboutTheObject
: a Record<K, V>
is an object with keys K
and values V
. So that constraint says "T
is a type whose properties must extend {propertyWithSameName: unknown}
, or "T
's properties must themselves have a propertyWithSameName
property".
I'm not sure if I really like the weird reduce()
function I put inside transform()
so feel free to change that if you want, but hopefully you get the idea how the typings should look.
Okay, hope that helps. Good luck!
Upvotes: 1