Given the following types:
type A = {
name: 'a';
id: string;
};
type B = {
name: 'b';
url: string;
};
type C = {
name: 'c';
id: string;
};
I want to manually create an object that looks like so:
{
a: {
pk: 'id'
},
b: {
pk: 'url'
},
c: {
pk: 'id'
}
}
But I want it typed so that it will automatically know the allowed top level property names (the "name" value from each type) as well as possible values for pk
(basically any property names in that type other than "name" are acceptable).
I can do it for one type like so:
type ABC = A | B | C;
type MyObject<T extends ABC> = {
[Key in T['name']]: {
pk: keyof Omit<T, 'name'>;
};
};
const x: MyObject<A> = {
a: {
pk: 'id',
},
};
That type will accept any one of the ABC types. But I don't know how to make it so that the object can contain all of the ABC types.
What I want is to type "a", and then when I go to enter its "pk" value, it knows the only potential value is "id". Then I add the "b" key to the same object, and it knows the only potential "pk" value is "url".
These are just sample types, the real ones have a lot more fields and there are a lot more types.
CodePudding user response:
I would take advantage of key remapping in mapped types to iterate over each member of your input union, like this:
type MyObject<T extends { name: string }> = {
[U in T as U['name']]: { pk: Exclude<keyof U, "name"> }
};
For A | B | C
, that evaluates to:
type M = MyObject<A | B | C>;
/* type M = {
a: {
pk: "id";
};
b: {
pk: "url";
};
c: {
pk: "id";
};
} */