jquense / yup

Dead simple Object schema validation
MIT License
22.72k stars 925 forks source link

ObjectSchema type vs ObjectSchema class vs object+shape #2200

Open hikeeba opened 5 months ago

hikeeba commented 5 months ago

What is the difference, and intended use case, for each of these 3 things? The documentation on ObjectSchema is very limited, and only appears to refer to it as a type and not as a class.

For example, what is the different between AccountSchemaA and AccountSchemaB in the code below?

interface Account {
  username: string;
  email: string;
  phone: string;
}

const AccountSchemaA = new yup.ObjectSchema<Account>({
  username: yup.string().required(),
  email: yup.string().required(),
  phone: yup
    .string()
    .required()
    .matches(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/, {
      message: 'Phone number must be in the format (XXX) XXX-XXXX',
      excludeEmptyString: true,
    }),
});

const AccountSchemaB: yup.ObjectSchema<Account> = yup.object({
  username: yup.string().required(),
  email: yup.string().required(),
  phone: yup
    .string()
    .required()
    .matches(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/, {
      message: 'Phone number must be in the format (XXX) XXX-XXXX',
      excludeEmptyString: true,
    }),
});

From what I can tell, adding an unexpected property to AccountSchemaA, like in the example below, gives an error, but this does not happen for AccountSchemaB.

The error is Object literal may only specify known properties, and 'bob' does not exist in type 'Shape<Account, AnyObject>'.ts(2353).

const AccountSchemaA = new yup.ObjectSchema<Account>({
  username: yup.string().required(),
  email: yup.string().required(),
  bob: yup.string(),
  phone: yup
    .string()
    .required()
    .matches(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/, {
      message: 'Phone number must be in the format (XXX) XXX-XXXX',
      excludeEmptyString: true,
    }),
});

Any details on this would be super helpful to make sure we are using this as intended and understand the tradeoffs between the two.

Thanks!

hikeeba commented 5 months ago

It may be that AccountSchemaB would throw an error at compile time with the added bob property, but for AccountSchemaA this error shows up immediately in the editor.

jquense commented 4 months ago

there is no difference in behavior, the types are just different on the class vs the factory function. In general the types are going to be less accurate if you use the schema class directly. There are a lot of generics meant to be used in particular ways on them. Prefer the factory functions unless you are comfortable with complex generics