const constants = {
filter: 'flr',
color: 'col'
} as const; // very important
type Constants = typeof constants; // represents the type of the constants object
type ConstantsValues = Constants[keyof Constants] // defines values of constants object
type MetaData = {
[K in ConstantsValues]: any // map which has all keys as values of constants object
}
// using
const meta: MetaData = {
flr: 1,
col: 2
}
const filterValue: number = meta[constants.filter];
Explanation
Most important is to define constants as const, it means that we are saying this structure of the object is permanent and the inferred types should be precise to exactly that. Thanks to const the typeof constants defines exact keys and values which the object has.
Next things are two types Constants and ConstantsValues which are just simple definitions of types from the constants object. They are here for readability purposes.
Last is MetaData. We define it as map which keys are all values of the constants object. I set values as any as you did not define the need about this.
Any change of the constants object will effect the need of changing every instance of Metadata type.
If the whole Metadata object should have one type of value, as you have shown in your example by number, it can be achieved by:
type MetaData = {
[K in ConstantsValues]: number // here we have a number for all values
}
or in more polymorphic way:
type MetaData<V> = {
[K in ConstantsValues]: V // here we have V for all values
}
type MetaDataStr = MetaData<string>
type MetaDataNum = MetaData<number>
Additional need was requested in the comment. The need is to be able to define types which will represent partial of the constants object with different type of values. Here is an example of implementing that:
// bigger object to have an example
const constants = {
filter: 'flr',
color: 'col',
rank: 'rnk',
size: 'sz'
} as const;
// the same two types as previous
type Constants = typeof constants;
type ConstantsValues = Constants[keyof Constants];
// create parts of values
type PartOfConstantsA = Extract<ConstantsValues, 'flr' | 'col'>
type PartOfConstantsB = Extract<ConstantsValues, 'rnk' | 'sz'>
// create more generic type in order to pass also keys by Keys generic
type MetaData<Keys extends PropertyKey, Values> = {
[K in Keys]: Values
}
// we combine both types by &
type FinalMetaData = MetaData<PartOfConstantsA, number> & MetaData<PartOfConstantsB, string>;
// using
const meta: FinalMetaData = {
flr: 1, // needs to be number
col: 2,// needs to be number
rnk: 'a', // needs to be string
sz: 'b' // needs to be number
}
const filterValue = meta[constants.filter];