Defining constants with predefined keys in typescript can be confusing at times. Let's walk through an example:
// suppose we have this object
const MY_LIB = {
id_1: {foo: 'foo', bar: 'bar'},
id_2: {foo: 'foo', bar: 'bar'}
} as const
// later on, we might want to create a type based on it, like this:
type LibIds = keyof typeof MY_LIB // 'id_1' | 'id_2'
Everything seems fine up to this point.
However, when dealing with large objects containing numerous keys and complex properties (such as nested objects), it becomes necessary to enforce type checking on the library object itself. For example:
// consider the following scenario
const MY_LIB: Record<string, {foo: string, bar: string}> = {
id_1: {foo: 'foo', bar: 'bar'},
id_2: {foo: 'foo', bar: 123} // error detected! - bar should be a string
} as const
// but now our type doesn't function properly anymore, it becomes of generic value "string"
type LibIds = keyof typeof MY_LIB // string
My ideal solution would look something like this:
type ReadonlyLibrary<Libtype> = // implementation details go here
const MY_LIB: ReadonlyLibrary<{foo: stringl bar: string}> = {
id_1: {foo: 'foo', bar: 'bar'},
id_2: {foo: 'foo', bar: 'bar'}
}
// MY_LIB is of type: {[key: 'id_1' | 'id_2']: {foo: stringl bar: string}}
// this approach aligns perfectly with my requirements
type LibKeys = keyof typeof MY_LIB // 'id1' | 'id_2'
How do you typically handle this situation? Are there any better techniques or strategies?