You want to define a type for an object which has keys matching every value in an enum and values that are different (string and number in your example). You want to enforce that every key matches the appropriate value, while also requiring that all keys have some value.
This can be a bit confusing since we are trying to enforce typings on a type rather than an object. But there are a few things we can do.
- We can separate the properties into groups by value type. Since we only have two value types here, we can define one as the leftovers from the other.
type NumberProperties = MyProperties.PROP_C;
type StringProperties = Exclude<MyProperties, NumberProperties>
type Foo = {
[K in NumberProperties]: number;
} & {
[K in StringProperties]: string;
}
Link
- We can use a helper type
MissingKeys to check at compile time that we haven't forgotten anything. We do need to check it ourselves, as no error is thrown.
enum MyProperties {
PROP_A = 'propA',
PROP_B = 'propB',
PROP_C = 'propC'
}
type Foo = {
[MyProperties.PROP_B]: string;
[MyProperties.PROP_C]: number;
}
type MissingKeys = Exclude<MyProperties, keyof Foo> // evaluates to MyProperties.PROP_A
Link
- My Favorite. We can add an declaration to our type that states that any missing properties must be set in objects implementing
Foo. Let's give it an illogical value so that we can notice that something is missing from Foo and adjust Foo accordingly. I like this one because we get a big red underline so it's obvious that the type is wrong.
type DefinedFoo = {
[MyProperties.PROP_B]: string;
[MyProperties.PROP_C]: number;
}
type MissingKeys = Exclude<MyProperties, keyof DefinedFoo> // evaluates to MyProperties.PROP_A
type Foo = DefinedFoo & {
[K in MissingKeys]-?: "missing property definition"
}
const fooImplementation1: Foo = {
[MyProperties.PROP_B]: "b",
[MyProperties.PROP_C]: 0,
}
// gives an error: Property 'propA' is missing...
const fooImplementation2: Foo = {
[MyProperties.PROP_A]: "a",
[MyProperties.PROP_B]: "b",
[MyProperties.PROP_C]: 0,
}
// gives an error: Type of computed property's value is '"a"', which is not assignable to type '"missing property definition"'
Link
type Foo = Record<MyProperties, string | number>;stringforpropAandpropB,numberforpropC.