Given the following list:
const list = ["A", "B", "C"] as const;
type List = typeof list[number];
I have a map that must have all of the possible keys of list:
const mapping: Record<List, unknown> = {
A: true,
B: 2,
C: "three"
};
Just like I could enforce mapping to map over List, I would like to do the same for a type. Something like this (I'm aware it's an invalid syntax):
type MappedList: Record<List, unknown> = {
A: boolean,
B: number,
C: string
}
My main goal is to prevent a situation when I add a new cell into list and forget to add it to MappedList.
CodePudding user response:
You can do this by creating a utility that would require generics to match:
type AssertKeysEqual<
T1 extends Record<keyof T2, any>,
T2 extends Record<keyof T1, any>
> = T2
const list = ["A", "B", "C"] as const;
type ListKey = typeof list[number];
const mapping: Record<ListKey, unknown> = {
A: true,
B: 2,
C: "three",
};
type MappedList = AssertKeysEqual<Record<ListKey, unknown>, {
A: boolean;
B: number;
C: string;
}>
CodePudding user response:
AFAIK, there is not such concept as type for type. However, you can use mapped types to create one type from another.
const list = ["A", "B", "C"] as const;
type ListKey = typeof list[number];
// type MappedList = {
// A: "property";
// B: "property";
// C: "property";
// }
type MappedList = {
[Prop in ListKey]: 'property'
}
As far as I understood, you also need to assure that A is a boolean, B is a number and C is a string. In order to do it, you need create a map and conditional type:
const list = ["A", "B", "C"] as const;
type ListKey = typeof list[number];
type TypeMap = {
A: boolean,
B: number,
C: string
};
/**
* If T is a subtype of TypeMap
* and keyof T extends keyof TypeMap
*/
type BuildMappedList<T> = T extends TypeMap ? keyof T extends keyof TypeMap ? T : never : never;
/**
* Ok
*/
type MappedList = BuildMappedList<{
A: true,
B: 2,
C: "three",
}>
/**
* Never
*/
type MappedList2 = BuildMappedList<{
A: true,
B: 2,
C: "three",
D: [2] // because of extra D property
}>
/**
* Never
*/
type MappedList3 = BuildMappedList<{
B: 2,
C: "three",
}> // because no A property
/**
* Never
*/
type MappedList4 = BuildMappedList<{
A: false,
B: [2], // because B is not a number
C: "three",
}>
