Definitelytyped: [@types/vuelidate] ValidationProperties type causing errors

Created on 20 Apr 2019  路  14Comments  路  Source: DefinitelyTyped/DefinitelyTyped

  • [x] I tried using the @types/vuelidate package and had problems.
  • [x] I tried using the latest stable version of tsc. https://www.npmjs.com/package/typescript
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @janesser @jubairsaidi

Issue:
I'm using @types/vuelidate and am getting multiple compiler errors.

Top-level validation models are possibly undefined:

Object is possibly 'undefined'.
    77 |       console.log(this.$v.form.nestedA);
    78 |       console.log(this.$v.flatA.required);
  > 79 |       console.log(this.$v.people.required);
       |                   ^
    80 |     },
    81 |   },
    82 | });

The ValidationProperties type is inheriting the type assigned to the data property:

Property 'required' does not exist on type 'Validation & ((Validation & string) | undefined)[]'.
    77 |       console.log(this.$v.form.nestedA);
    78 |       console.log(this.$v.flatA.required);
  > 79 |       console.log(this.$v.people.required);
       |                                  ^
    80 |     },
    81 |   },
    82 | });

Example component code:
(data copied/simplified from the vuelidate-tests.ts file within the repo)

import Vue from 'vue';
import { required } from 'vuelidate/lib/validators';

interface Data {
  forGroup: {
    nested: string;
  };
  password: string;
  form: {
    nestedA: string;
    nestedB: string;
  };
  flatA: string;
  people: string[];
  validationGroup: string[];
}

export default Vue.extend({
  name: 'FormComponent',
  data(): Data {
    return {
      forGroup: {
        nested: '',
      },
      password: '',
      form: {
        nestedA: '',
        nestedB: '',
      },
      flatA: '',
      people: ['Pierre', 'Paul', 'Jacques'],
      validationGroup: ['password', 'flatA', 'forGroup.nested'],
    };
  },
  validations: {
    password: {
      required,
    },
    form: {
      nestedA: {
        required,
      },
      nestedB: {
        required,
      },
    },
    flatA: { required },
    people: {
      required,
      $each: {
        name: {
          required,
        },
      },
    },
  },
  computed: {
    disableTermDateField(): void {
      console.log(this.$v.password.required);
      console.log(this.$v.form.nestedA);
      console.log(this.$v.flatA.required);
      console.log(this.$v.people.required);
    },
  },
});

I can get rid of the errors by changing

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bed07e18cac15a36a60186fe97afa356b517eac0/types/vuelidate/vue.d.ts#L8-L10

by removing the optional property marker and changing the type parameter to any. But I'm not sure if that would have more wide-spread impact.

type ValidationProperties<V> = {
    [P in keyof V]: Validation & ValidationProperties<any> // New
};

Most helpful comment

If I want to call this.$v.email.required without typescript complaining, how do I do that? It's been hours...

All 14 comments

I have played a lot with this part, and there are limitations to recursive types for sure.

As far as I remember:
a) there are testcases around here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bed07e18cac15a36a60186fe97afa356b517eac0/types/vuelidate/vuelidate-tests.ts#L147
I cannot yet say where your case is different.
b) you might want to double-check your tsconfig.json, take the present one as a reference https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bed07e18cac15a36a60186fe97afa356b517eac0/types/vuelidate/tsconfig.json
c) if your problems persist, please provide some reproducible case in vuelidate-tests.ts mentioned above.

Regards,
Jan

If you remove the conditional around this section of the test cases, you will see the errors I've been getting about this.$v.form being possibly undefined:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bed07e18cac15a36a60186fe97afa356b517eac0/types/vuelidate/vuelidate-tests.ts#L146-L152

Error:

Object is possibly 'undefined'.
    223 |
    224 |       // if (this.$v.form) {
  > 225 |       console.log(this.$v.form.$params);
        |                   ^
    226 |       console.log(this.$v.form.$params.nestedA);
    227 |       console.log(this.$v.form.nestedA);
    228 |       console.log(this.$v.form.$params.nestedOfLength);

Is there a reason why the ValidationProperties type is written in a way that requires not-undefined checks in cases like that? It would be nice to avoid wrapping all references to this.$v[PROPERTY] in a conditional and it seems like it could be avoided by removing the optional property marker ? on line 9 of the vue.d.ts file:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bed07e18cac15a36a60186fe97afa356b517eac0/types/vuelidate/vue.d.ts#L8-L10

Un tested, but I think you can have this.$v with no properties, i.e. take any component with no validation rules specified. Its this.$v should still exist but not have any properties.

I think this would be a good test: If this.$v is defined but has no properties when you don't include type definitions, then the current definition is correct.

Have you tried type ValidationProperties<V> = { [P in keyof V]: Validation & ValidationProperties<V[P]> };

If you get no errors from the above tests after removing the optional (keeping the nesting as is, it's important for how vuelidate works), feel free to submit a PR and include test coverage that includes:

component with:

  • [ ] no validation rules with component attribute of form, this.$v.form should fail as undefined.

If I want to call this.$v.email.required without typescript complaining, how do I do that? It's been hours...

@signal-intrusion what is the complaint? "Possibly undefined" ? See if statement in above example. That could help.

it shouldn't if you specify the validation rules, @signal-intrusion mind posting your component code so we can take a look?

Hey,
I just stumbled upon this. Since it seems like no one else wants to explain the problem any further I'll give it a shot and explain my perspective :D

First of all
I'm using version 0.76 of @types/vuelidate, version 0.7.4 of vuelidate and version 3.5.3 of typescript.

The Problem
The first part of the issue (... is possibly 'undefined'.) is not the problem. That's easily fixed by checking if it's undefined. The problem (at least for me) is the second error in the issue Property 'required' does not exist on type 'Validation & string'.

In the following example typescript complains about the lines inside the computed property. So for each line from 38 to 40 there is an error that required / minLength / maxLength does not exist on this.$v.name. (Property 'maxLength' does not exist on type 'Validation & string'.)

https://gist.github.com/JonasSchade/40c0e13fdb50acce3bc239f99c385c37#file-vuelidate-typescript-test-vue-L38
(I usually use Vuetify and to use vuelidate and Vuetify together to have to pass an array with error strings to the Vuetify component. But I thought it would be easier for you guys if I keep it vanilla and just show the array below the input.)

Solving this issue
Solving this issue is kinda tricky and as far as I'm concerned the only solution would be an index signature like [index: string]: boolean for the Validation interface, because basically you can name your validation for fields anything you want. But I'm rather inexperienced with typescript so I'll leave the solutions to the smart guys. :wink:

@JonasSchade i would be glad if you could confirm that things are straighten out now. Regards, Jan.

@janesser Code looks good to me and a quick test showed that it now works as expected.

Thanks a lot :)

The types for this seem somewhat broken.

ERROR in /Volumes/SuperData/Sites/reelcrafter/reelcrafter-ui/src/components/ui/RepContactEditorFields.vue
134:18 Property 'required' does not exist on type 'boolean'.
    132 |
    133 |       if (!field.$error) return '';
  > 134 |       if (!field.required && fieldName !== 'email') {

This is the code:

      if (!this.$v || !this.$v.fields) return '';

      const field = this.$v.fields[fieldName];
      const fieldRef = this.$refs[fieldName] as any;

      if (!field.$error) return '';
      if (!field.required && fieldName !== 'email') {
        return this.$t('global.errors.requiredFieldTxt', {
          name: fieldRef.label,
        }).toString();
      }

How in the world does it think this is a boolean?!

The types for this seem somewhat broken.

ERROR in /Volumes/SuperData/Sites/reelcrafter/reelcrafter-ui/src/components/ui/RepContactEditorFields.vue
134:18 Property 'required' does not exist on type 'boolean'.
    132 |
    133 |       if (!field.$error) return '';
  > 134 |       if (!field.required && fieldName !== 'email') {

This is the code:

      if (!this.$v || !this.$v.fields) return '';

      const field = this.$v.fields[fieldName];
      const fieldRef = this.$refs[fieldName] as any;

      if (!field.$error) return '';
      if (!field.required && fieldName !== 'email') {
        return this.$t('global.errors.requiredFieldTxt', {
          name: fieldRef.label,
        }).toString();
      }

How in the world does it think this is a boolean?!

@life777 If you could please provide the whole example as gist or something?
Regarding your example it is not very clear what "fields" is.

Typescript union types have some cave-eats... to be continued.

@janesser @jubairsaidi
We're approaching the end of 2020 and the issue is still there. Is there any chance of getting rid of the "Object is possibly 'undefined'." when trying to access a nested validations field, for instace $v.form.fields.email.$invalid? It makes the library unusuable at the moment when using Typescript and nested validations config (or usable if we accept these ugly warnings).

I'd be truly grateful for any feedback on this matter from you guys :)

@DamianGlowala at the time the type-mapping was made the typescript compiler offered no way to reflect these nested-structure applying proper typing to it.

The trade-off made was to tell the compiler there is a union-type of some types we know well or "something". That "something" triggers the undefined-protection-warnings even thought you might navigate on one of the more typed-forks of the union type.

Not sure if there has been a change on the situation of typescript typesystem.

@janesser thank you for all of the time and effort you've put into keeping this package's types updated!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JWT
svipas picture svipas  路  3Comments

csharpner picture csharpner  路  3Comments

variousauthors picture variousauthors  路  3Comments

victor-guoyu picture victor-guoyu  路  3Comments

tyv picture tyv  路  3Comments