I am trying to write a generic type that will enforce the type of a given property (value
) based on the value of another (field
), where the value of field
is a key in the type provided as the generic's parameter.
Here is what I have tried:
type Rule<M, F extends keyof M = keyof M> = {
field: F;
value: M[F];
};
Usage:
interface MyModel {
firstName: string;
lastName: string;
age: number;
isActive: boolean;
}
const rule1: Rule<MyModel> = {
field: 'age',
value: 'twentynine'
// ^^ string | number | boolean
};
Currently, the type of rule1.value
is a union of all types in MyModel
, no matter what the value of rule1.field
is.
Instead, I'm hoping for rule1.value
to be of type number
because the value "age" I have provided to rule1.field
is a key in MyModel
and the type is number
.
The following examples would have no errors:
const rule2: Rule<MyModel> = {
field: 'age',
value: 29
};
const rule3: Rule<MyModel> = {
field: 'firstName',
value: 'George'
};
CodePudding user response:
Your approach doesn't work because the F
type can't be inferred from the object assignation.
Here is a possible solution to your problem which consists in using mapped types in order to build a union type with every entry of your interface:
type Rule<T> = {
[K in keyof T]: { field: K, value: T[K] };
}[keyof T];
interface MyModel {
firstName: string;
lastName: string;
age: number;
isActive: boolean;
};
const rule1: Rule<MyModel> = {
field: 'age',
value: 'twentynine'
};
const rule2: Rule<MyModel> = {
field: 'age',
value: 29
};