Saeed Khazaei
Saeed Khazaei

Reputation: 26

Difference between yup infer and what I need to be as my typeface

Based on my types and schemas, I need to have conditional schema which helps me to returns its keys based on select gender, but It does not work correctly.

yup: 1.4.0


export enum PersonGender {
  Male = "male",
  Female = "female",
}

export const genderValues = Object.values(PersonGender);

type PersonBase = {
  name: string;
  family: string;
  age: number;
  gender: PersonGender;
};

type FemalePerson = {
  characters: string[];
};

type MalePerson = {
  activities: string[];
};

export type Person = PersonBase & (MalePerson | FemalePerson);

export const baseSchema: yup.ObjectSchema<PersonBase> = yup.object().shape({
  name: yup.string().required(),
  family: yup.string().required(),
  age: yup.number().required(),
  gender: yup
    .string()
    .oneOf(genderValues)
    .required()
    .default(PersonGender.Male),
});

export const maleSchema: yup.ObjectSchema<MalePerson> = yup.object().shape({
  activities: yup.array().of(yup.string().required()).min(1).required(),
});

export const femaleSchema: yup.ObjectSchema<FemalePerson> = yup.object().shape({
  characters: yup.array().of(yup.string().required()).min(1).required(),
});

export const personSchema = yup.lazy((value) => {
  switch (value.gender) {
    case PersonGender.Male:
      return baseSchema.concat(maleSchema);
    case PersonGender.Female:
      return baseSchema.concat(femaleSchema);
    default:
      return baseSchema;
  }
});

export type PersonSchemaForm = yup.InferType<typeof personSchema>;

type of PersonSchemaForm is not the same as I need to have like Person

Upvotes: 0

Views: 38

Answers (1)

Caleth
Caleth

Reputation: 62694

yup is inferring PersonBase as the result of that schema, because you return baseSchema on it's own in the default case. You can think of the inferred type as being (PersonBase & MalePerson) | (PersonBase & FemalePerson) | (PersonBase) corresponding to the three different returns. This type simplifies to PersonBase.

If you instead only ever return baseSchema.concat(maleSchema) or baseSchema.concat(femaleSchema), then the inferred type is equivalent to Person.

export const personSchema = yup.lazy((value) => {
  switch (value.gender) {
    default: // Male is the default value for gender in baseSchema
    case PersonGender.Male:
      return baseSchema.concat(maleSchema);
    case PersonGender.Female:
      return baseSchema.concat(femaleSchema);
  }
});

Upvotes: 0

Related Questions