Home > Software design >  How to ensure a TypeError if the value provided is not of the type implied by another property
How to ensure a TypeError if the value provided is not of the type implied by another property

Time:03-19

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
};

TypeScript playground

  • Related