Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Define an object with fixed keys in Typescript

Given a situation where you have a data structure that is a map of different configuration values:

interface TabConfig {
  label: string
  slug: string
  color: string
}

const tabs = {
  home: {
    label: 'Home',
    slug: 'site-home',
    color: 'blue'
  } satisfies TabConfig,
  about: {
    label: 'About',
    slug: 'about-us',
    color: 'blue'
  } satisfies TabConfig,
  contact: {
    label: 'Contact Us',
    slug: 'contact',
    color: 'green'
  } satisfies TabConfig,
}
type TabName = keyof typeof tabs

This has the effect that TabName is the literal 'home' | 'about' | 'contact' instead of string (which is good for further type checking to ensure it is truly the name of one of the saved configurations).

The thing I’m not thrilled about this setup is the requirement to remember to put the satisfies TabConfig on every entry. Having that annotation helps make sure none of the properties are misspelled or forgotten. But it adds a lot of repeating satisfies boilerplate.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Changing the tabs type to be const tabs: { [tabName: string]: TabConfig } = {... allows dropping the satisfies declarations, but then TabName becomes string | number (too generic).

Changing the tabs type to be const tabs: Record<string, TabConfig> = {... also allows dropping the satisfies declarations, but then TabName becomes string (too generic).

A few examples I’ve seen manually declare type TabName = 'home' | 'about' | 'contact', but to me that method seems cumbersome for adding new items to the set, since you have to remember to add the new name in two places (once in TabName type definition, and then again in tabs).

So, is there a more concise way to declare this configuration mapping to have values that are a specific type, and also end up with a type that is just the compile-time keys present in that mapping, more concisely than using satisfies on every declaration?

>Solution :

You can use satisfies on the top level, not for each property:

const tabs = {
  home: {
    label: 'Home',
    slug: 'site-home',
    color: 'blue'
  } ,
  about: {
    label: 'About',
    slug: 'about-us',
    color: 'blue'
  } ,
  contact: {
    label: 'Contact Us',
    slug: 'contact',
    color: 'green'
  } ,
} satisfies Record<string, TabConfig>
// type TabName = "home" | "about" | "contact
type TabName = keyof typeof tabs
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading