Repository: logaretm/vee-validate (TypeScript)

Issue:
meta.required doesn't work on number().required() schema in yup
### What happened?

I really appreciate your last feature, which added `meta.required`, which is returned on `useField()`, when I implemented new design I noticed the `meta.required` is always `false`, when I have input which stores value in `Number` type and I use `number().required()` as validation rule in yup.

Also:
Couldn't be `meta.required` reactive, so value of it will change depending on `when()` is at this moment true or false? It would be nice so my design of inputs would change (I would reactively change inputs design) depending if input is required or not.

### Reproduction steps

1. useForm with validation on input with `number().required()`
2. Meta required seems to be false, however works without problem on `string().required()
...

You can change validation type in schema from ` number()` to `string()` and meta will be correct

### Version

Vue.js 3.x and vee-validate 4.x

### What browsers are you seeing the problem on?

- [ ] Firefox
- [x] Chrome
- [ ] Safari
- [x] Microsoft Edge

### Relevant log output

_No response_

### Demo link

https://stackblitz.com/edit/vue-nqdv8g?file=src%2FApp.vue

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](CONDUCT.md)


Discussion:
Looks like there is a more reliable way to figure that out through `spec` property. Thanks for reporting this.



Patch:
diff --git a/.changeset/angry-steaks-itch.md b/.changeset/angry-steaks-itch.md
new file mode 100644
index 000000000..b327078ce
--- /dev/null
+++ b/.changeset/angry-steaks-itch.md
@@ -0,0 +1,5 @@
+---
+"@vee-validate/yup": patch
+---
+
+fix: use spec to find required state closes #4598
diff --git a/packages/yup/src/index.ts b/packages/yup/src/index.ts
index 5e4410601..3ece1876d 100644
--- a/packages/yup/src/index.ts
+++ b/packages/yup/src/index.ts
@@ -1,12 +1,4 @@
-import {
-  AnyObjectSchema,
-  ArraySchema,
-  InferType,
-  Schema,
-  SchemaFieldDescription,
-  ValidateOptions,
-  ValidationError,
-} from 'yup';
+import { AnyObjectSchema, ArraySchema, InferType, Schema, ValidateOptions, ValidationError } from 'yup';
 import {
   TypedSchema,
   TypedSchemaError,
@@ -76,10 +68,10 @@ export function toTypedSchema<TSchema extends Schema, TOutput = InferType<TSchem
     },
     describe(path) {
       if (!path) {
-        return getDescriptionFromYupDescription(yupSchema.describe());
+        return getDescriptionFromYupSpec(yupSchema.spec);
       }
 
-      const description = getDescriptionForPath(path, yupSchema);
+      const description = getSpecForPath(path, yupSchema);
       if (!description) {
         return {
           required: false,
@@ -87,30 +79,21 @@ export function toTypedSchema<TSchema extends Schema, TOutput = InferType<TSchem
         };
       }
 
-      return getDescriptionFromYupDescription(description);
+      return getDescriptionFromYupSpec(description);
     },
   };
 
   return schema;
 }
 
-function getDescriptionFromYupDescription(desc: SchemaFieldDescription): TypedSchemaPathDescription {
-  if ('tests' in desc) {
-    const required = desc?.tests?.some(t => t.name === 'required') || false;
-
-    return {
-      required,
-      exists: true,
-    };
-  }
-
+function getDescriptionFromYupSpec(spec: AnyObjectSchema['spec']): TypedSchemaPathDescription {
   return {
-    required: false,
-    exists: false,
+    required: !spec.optional,
+    exists: true,
   };
 }
 
-function getDescriptionForPath(path: string, schema: Schema): SchemaFieldDescription | null {
+function getSpecForPath(path: string, schema: Schema): AnyObjectSchema['spec'] | null {
   if (!isObjectSchema(schema)) {
     return null;
   }
@@ -118,7 +101,7 @@ function getDescriptionForPath(path: string, schema: Schema): SchemaFieldDescrip
   if (isNotNestedPath(path)) {
     const field = schema.fields[cleanupNonNestedPath(path)];
 
-    return field?.describe() || null;
+    return (field as AnyObjectSchema)?.spec || null;
   }
 
   const paths = (path || '').split(/\.|\[(\d+)\]/).filter(Boolean);
@@ -133,7 +116,7 @@ function getDescriptionForPath(path: string, schema: Schema): SchemaFieldDescrip
     }
 
     if (i === paths.length - 1) {
-      return currentSchema.describe();
+      return currentSchema.spec;
     }
   }
 


Test patch:
diff --git a/packages/yup/tests/yup.spec.ts b/packages/yup/tests/yup.spec.ts
index 54f5834f5..bad35b41f 100644
--- a/packages/yup/tests/yup.spec.ts
+++ b/packages/yup/tests/yup.spec.ts
@@ -331,6 +331,7 @@ test('reports required state on fields', async () => {
         yup.object({
           'not.nested.req': yup.string().required(),
           name: yup.string(),
+          num: yup.number().required(),
           email: yup.string().required(),
           nested: yup.object({
             arr: yup.array().of(yup.object({ req: yup.string().required(), nreq: yup.string() })),
@@ -347,6 +348,7 @@ test('reports required state on fields', async () => {
       });
 
       const { meta: name } = useField('name');
+      const { meta: num } = useField('num');
       const { meta: email } = useField('email');
       const { meta: req } = useField('nested.obj.req');
       const { meta: nreq } = useField('nested.obj.nreq');
@@ -356,6 +358,7 @@ test('reports required state on fields', async () => {
 
       metaSpy({
         name: name.required,
+        num: num.required,
         email: email.required,
         objReq: req.required,
         objNreq: nreq.required,
@@ -376,6 +379,7 @@ test('reports required state on fields', async () => {
     expect.objectContaining({
       name: false,
       email: true,
+      num: true,
       objReq: true,
       objNreq: false,
       arrReq: true,