Alex's Slip-box

These are my org-mode notes in sort of Zettelkasten style

Constants, interfaces and types

See also Index signature

# const Assertions

Using as const lets you define literal values.

  • For objects the keys are readonly
  • Arrays become readonly tuples.
const THINGS = {
  thing1: 1,
  thing2: 2
} as const

# With types

Here’s one problem I had. This trivial example demonstrates it. Doing something like this…

const thingName = Object.keys(THINGS).find((name) => {
  return THINGS[name] === 1

…TypeScript will fail to compile with this error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ readonly thing1: 1; readonly thing2: 4; }'.
No index signature with a parameter of type 'string' was found on type '{ readonly thing1: 1; readonly thing2: 4; }'.

OK, so we have literal values here and TypeScript doesn’t know what the name local variable is. To get around this, I defined a union type out of the constants keys.

# typeof

type ThingName = keyof typeof THINGS; // 'thing1' | 'thing2'

Then, using a type assertion on the name, satisfied the compiler:

const thingName = Object.keys(THINGS).find((name) => {
  return THINGS[name as ThingName] === 1

With an Array const:

const things = ['thing1', 'thing2'] as const;
type thing = typeof things[number];

See also

# valueof

There isn’t a valueof, but a helper type can be created to accomplish that:

type ValueOf<T> = T[keyof T]
type ValueOfThings = ValueOf<Things>; // 1 | 2

See also

# With Record utility type

I didn’t actually do this, but it looks like it’s possible to use a Record type if I really wanted to get crazy with it.

type ThingName = 'thing1' | 'thing2'
type ThingId = 1 | 2

export const THINGS: Record<ThingName, ThingId> = {
  thing1: 1,
  thing2: 2
} as const