typescriptvue.jsvuejs3v-model

VueJS Typescript v-model Type 'number' is not assignable to type 'Nullable<string>'


So I'm new to Typescript and VueJS and all of its new features.
Everything works as it is supposed to be but I cannot get rid of my typescript error and using v-model at the same time.
I'm working on a webview for a member to check and change its attributes. I get my members data from an API and store it in a PiniaStore. This means I have several InputFields requiring numbers and strings for a member. AFAIK v-model is the way to go for InputFields.

 Type 'string | number | null | undefined' is not assignable to type 'Nullable<string>'.
  Type 'number' is not assignable to type 'Nullable<string>'.

All the suggested solutions of stackoverflow questions to this error like this one or this one don't fit to my problem AFAIK. I found a bad workaround, which I don't like using a Change Event instead of v-model in my template block and having the same error in my script but ignore it via //@ts-ignore.

First, all I really ask for is how to comment out a typescript error in a VueJs template block, already asked here.
Second, how do I solve this problem without having a typescript error?

Looking at the piece of code below, I have this error at v-model and don't know how to fix it:

<script setup lang="ts">
import { useMembershipStore } from "@/stores/membership";

const membershipStore = useMembershipStore();
membershipStore.getMembership();
const { membership } = storeToRefs(membershipStore);

function save() {
  if (membership.value) {
    membershipStore.updateMembership(membership.value);
  }
}
</script>


<template>
  <div v-if="membership === null" class="loading">
    <h2>Loading</h2>
  </div>
  <div v-else class="members-table">
    <div v-for="(value, key) in Object.keys(membership)" >
      <br />
      <InputText type="text"
        v-model="membership[value as keyof typeof membership]"
      />
    </div>
    <Button @click="save()" />
  </div>
</template>

Here are my type definitions: membershipstore.ts

export type MembershipStoreState = {
  membership: Member | null;
};

types.ts

export interface Member {
  id?: number;
  user_id?: string;
  user_attr: string | null;
  create_attr?: string | null;
  admin_attr?: string | null;
}

I also figured out where the type Nullable<string> comes from. It is from PrimeVues type definition of its component InputText, which can be found here:

export interface InputTextProps extends InputHTMLAttributes {
    /**
     * Value of the component.
     */
    modelValue?: Nullable<string>;
}

Full code example can be found here
Full code example with bad workaround using a change event, here


Solution

  • I suppose you are defining membership: Member|null to check for loader <div v-if="membership === null" class="loading">

    You can try this instead

    export type MembershipStoreState = {
      membership: Member;
    };
    

    In HTML

    <template>
      <div v-if="Object.keys(membership).length === 0" class="loading">
        <h2>Loading</h2>
      </div>
      <div v-else class="members-table">
        <div v-for="(value, key) in membership" > // thanks to @Dimava's comment
          <br />
          <InputNumber v-if = "key === 'id' || key === 'user_id'"
            v-model="membership[key]"
          />
          <InputText type="text" v-else
            v-model="membership[key]"
          />
        </div>
        <Button @click="save()" />
      </div>
    </template>
    

    Object.keys(membership).length === 0 Will check if you have popullated any of the keys of your interface otherwise it will return true and your loading will be visible