A new-ish addition to TypeScript is the as const
clause, which specifies objects and arrays as compile-time, readonly constant values, whose types are atomically specific.
const x: {
key: "value"
} as const
is represented by the type
readonly { "key": "value" }
This is a superb feature, allowing const
object declarations which I can use for other types.
However when I need to declare an map of types as compile-time constants, which extends a Record
type, it seems that the as const
clause and the Record
type have opposite effects.
enum Enum {
key1,
key2,
}
const staticMap: Record<Enum, string> = {
[Enum.key1]: "value1",
[Enum.key2]: "value2"
} as const;
yields the type Record<Enum, string>
, despite being marked as const
.
Omitting the type gives the correct type annotation, however I lose the restriction to use members of Enum
as keys to staticMap
.
To be honest, I'm not sure how to proceed from here. I'm wondering whether's some sort of halfway point, such that the following snippet fails:
const x: <all values must be assignable to `string`> = {
[Enum.key1]: "string",
[Enum.key2]: 123
} as const
Thanks for any pointers
CodePudding user response:
If you specify the type of a variable, that is it's final type, so as long as the expression on the right is assignable to it, everything is fine.
In this case you might want to remove the as const
and specify the fact that the record is readonly:
enum Enum {
key1,
key2,
}
const staticMap: Readonly<Record<Enum, string>> = {
[Enum.key1]: "value1",
[Enum.key2]: "value2"
};
The version above does not fully emulate as const
. If you want to preserve the value types as well you will need to use a generic function to capture the original value passed in for each property. This is closer to the behavior of as const
:
enum Enum {
key1,
key2,
}
function makeRecord<T extends Record<Enum, V>, V extends string>(o: T): Readonly<T> {
return o
}
const staticMap = makeRecord({
[Enum.key1]: "value1",
[Enum.key2]: "value2"
});
const x: typeof staticMap[Enum.key1] = "value2"; // err