Let’s say I have an object that I want to make sure its keys conform to another type. I’m creating a dictionary of mapped keys, where on the left I have the original, and on the right, the displayed key. I need this available in both runtime and compile time, so doing this as const
objects and arrays seems to make sense?
(Yes, I tried doing this with an array of tuples, but TypeScript is mad at me now).
const allowedKeys = ["cheese_type", "jalapeno_qty", "pepperoni"] as const;
const keyMap = {
cheese_type: "Cheese type",
jalapeno_qty: "Number of jalapeños",
pepperoni: "Pepperoni",
} as const;
This gives me a type that has all the keys mapped out explicitly:
const keyMap: {
readonly cheese_type: "Cheese type";
readonly jalapeno_qty: "Number of jalapeños";
readonly pepperoni: "Pepperoni";
}
(I can export the types, too)
export const Keys = typeof allowedKeys
export const KeyMap = typeof keyMap
But I also want to protect the keyMap
from accidentally misspelling or adding keys that it shouldn’t have, so I want to make it conform to the allowedKeys
array.
But if I do that, I can’t figure out how to infer or define the object values as readonly/const properties…
const allowedKeys = ["cheese_type", "jalapeno_qty", "pepperoni"] as const;
type Keys = typeof allowedKeys;
const keyMap: {
[key in Keys]: string // oh no
} = {
cheese_type: "Cheese type",
jalapeno_qty: "Number of jalapeños",
pepperoni: "Pepperoni",
} as const;
Is there any way I can do this that doesn’t make me run into crazy cycles like:
const keyMap: {
[key in Keys]: key in keyMap ? keyMap[key] : never; // haha no
} = {
cheese_type: "Cheese type",
jalapeno_qty: "Number of jalapeños",
pepperoni: "Pepperoni",
} as const;
(Ultimately, I want to be able to have two sets of keys I can reference…)
export type RawKey = typeof keyMap;
export type DisplayKey = ValueOf<typeof keyMap>;
2
Answers
You can do:
This will prevent additional keys from being added to the
keyMap
.EDIT:
In regards to pinks comment, you can do
to make the
keyMap
readonly.You can use the
satisfies
operator to check that a value matches a particular type without widening it to that type which can throw away information you want.So as long as you have the type
Keys
of the keys you want to allow:You can say that
keyMap
satisfies Record<Keys, string>
:and then
keyMap
still remembers all the literal types of its properties:And if you make mistakes with
keyMap
, the compiler should catch them:Playground link to code