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.
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