Home > OS >  Record types as compile-time constant
Record types as compile-time constant

Time:11-18

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"
};

Playground Link

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

Playground Link

  • Related