Typescript Scratchpad

This is by no means an article that's going to teach you the ins and outs of Typescript. Instead, this is more of my scratchpad for common patterns I've found myself using. I find myself revisiting codebases I haven't touched in a while searching for a helpful tidbit I've used in the past, so I'll be documenting them here.

Records

I found records are helpful when storing data that might be changed later. Often, a list will get updated with a new value but a I won't know all of the places that will need to be updated. A record helps with that by showing an error at runtime if a value is missing.

type LabelShape = 'Rectangle' | 'Oval' | 'Circle'

const LabelSelectItem: Record<LabelShape, JSX.Element> = {
  Rectangle: <span>Rectangle</span>,
  Oval: <span>Oval</span>,
  Circle: <span>Circle</span>,
}

const SelectLabel: FunctionComponent<SelectLabelProps> = () => {
  return (
    <div className="flex flex-col">
      <p className="pb-3 pl-1 text-sm">Select your shape</p>
      <Select>
        <SelectTrigger className="w-[300px]">
          <SelectValue placeholder="Label Shapes" />
        </SelectTrigger>
        <SelectContent>
          {Object.keys(LabelSelectItem).map((shape) => (
            <SelectItem key={shape} value={shape}>
              {shape}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
    </div>
  )
}

If the LabelShape type is updated to include a new value, we'll get an error at runtime that helps us easily identify what needs to be updated.

type LabelShape = 'Rectangle' | 'Oval' | 'Circle' | 'Triangle'

TS Enum Alternative

Enums are a great way to define a set of values that are related. However, they have some limitations.

const LabelShapes = {
  Circle: 'CIRCLE',
  Square: 'SQUARE',
} as const

type LabelShapes = (typeof LabelShapes)[keyof typeof LabelShapes]

This allows us to dot syntax to access the values safely while delcaring the values in a single definition.

const LabelShapes = {
  Circle: 'CIRCLE',
  Square: 'SQUARE',
} as const

type LabelShapes = (typeof LabelShapes)[keyof typeof LabelShapes]

interface SelectedLabelProps {
  labelShapes: LabelShapes
}

const SelectedLabel: FunctionComponent<SelectedLabelProps> = ({
  labelShapes,
}) => {
  switch (labelShapes) {
    case LabelShapes.Circle:
      return <p>You selected a circle label.</p>
    case LabelShapes.Square:
      return <p>You've selected a square label.</p>
    default:
      return <p>Please select a label shape.</p>
  }
}