Nuxt3 typescript props possibly undefined ! how to correctly declare component props using custom types?

109 Views Asked by At

I am trying to use typescript for a Nuxt3 project and it is my first experience with Typescript.

I have this component that aims at creating or editing a person in the backend:

<template>
  <UCard>
    <template #header>
      <h3 class="font-semibold text-xl">
        {{
          props.mode === "create"
            ? "Ajouter une personne"
            : "Modifier une personne"
        }}
      </h3>
    </template>

    <UForm :schema="schema" :state="state" class="space-y-2" @submit="onSubmit">

      <UFormGroup class="" label="Prénom" name="firstName">
        <UInput v-model="state.firstName" :disabled="props.mode === 'update'" />
      </UFormGroup>

      <UFormGroup class="" label="Nom" name="lastName">
        <UInput v-model="state.lastName" :disabled="props.mode === 'update'" />
      </UFormGroup>

      <UFormGroup class="" label="Email" name="email">
        <UInput v-model="state.email" />
      </UFormGroup>

      <UFormGroup class="" label="Téléphone" name="phone">
        <UInput v-model="state.phone" />
      </UFormGroup>

      <UFormGroup class="" label="Membre depuis" name="memberSince">
        <UInput v-model="state.memberSince" type="date" />
      </UFormGroup>

      <UFormGroup class="" label="Membre l'an dernier?" name="memberLastYear">
        <USelect v-model="state.memberLastYear" :options="mlyOptions" />
      </UFormGroup>
      <div class="inline-block flex-inline">
        <UButton type="submit"> Enregistrer cette personne </UButton>
        <UButton
          color="gray"
          variant="ghost"
          icon="i-heroicons-x-mark-20-solid"
          class="-my-1"
          @click="$emit('close')"
        />
      </div>
    </UForm>
    <template #footer> </template>
  </UCard>
</template>

<script setup lang="ts">
import type { FormError, FormSubmitEvent } from "#ui/types";
import { z } from "zod";
import { usePersonsStore } from "@/stores/persons.js";
import { type IPerson } from "~/types/IPerson";

const props = defineProps({
  mode: String,
  person: { type: Object as () => IPerson },
});
const personsStore = usePersonsStore();
const emit = defineEmits(["close"]);

const mlyOptions = ["Oui", "Non"];
const schema = z.object({
  firstName: z
    .string()
    .regex(
      /^[ÈÉÊA-Z][a-zïèéê']*([\-\s]{1}[ÈÉÊA-Z]{1}[a-zïèéê]*)*$/,
      "Capitale en début de chaque partie du prénom uniquement. Pas de double espace ou tiret. Pas d'espace ou de tiret en début et fin"
    ),
  lastName: z
    .string()
    .regex(
      /^([ÈÉÊA-Z]){1}('[ÈÉÊA-Z]+){0,1}[a-zïèéê']*([\-\s]{1}[ÈÉÊA-Z]{1}[a-z'ïèéê]*)*$/,
      "Capitale en début de chaque partie du nom. Pas de double espace ou tiret. Pas d'espace ou de tiret en début et fin. Apostrophe des noms irlandais acceptée."
    )
    .toUpperCase(),

  email: z.string().email("L'adresse électronique est mal formée"),
  phone: z
    .string()
    .trim()
    .transform((val, ctx) => {
      val = val.replace(/\s/g, "");
      if (val.length !== 10) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: "You must enter 10 digits",
        });
        return z.NEVER;
      }
      var parts = val.match(/.{1,2}/g);

      state.phone = parts.join(" ");
      return state.phone;
    }),
  memberSince: z.coerce.date(),
  memberLastYear: z.string(),
});

type Schema = z.output<typeof schema>;

const state: IPerson = reactive({
  id: undefined,
  firstName: "",
  lastName: "",
  email: "",
  phone: "",
  memberSince: "",
  memberLastYear: "",
});
if (props.mode === "update") {
  state.id = props.person.id;
  state.firstName = props.person.firstName;
  state.lastName = props.person.lastName;
  state.email = props.person.email;
  state.phone = props.person.phone;
  state.memberSince = props.person.memberSince.substring(0, 10);
  state.memberLastYear = props.person.memberLastYear;
}

async function onSubmit(event: FormSubmitEvent<any>) {
  console.log("submit");

  const { data, pending, error } = await useFetch("api/person", {
    method: props.mode === "create" ? "POST" : "PUT",
    body: event.data,
  });

  if (data) {
    console.log("logging data with mode " + props.mode);
    console.log(toRaw(data.value));
    if (props.mode === "create") {
      personsStore.addPerson(toRaw(data.value));
    } else {
      personsStore.updatePerson(toRaw(data.value));
    }
  }
  if (error) {
    console.log("logging error");
    console.log(error.value);
  }

  emit("close");
}
</script>

Here is the IPerson interface:

export interface IPerson {
    id?: number;
    firstName: string;
    lastName: string;
    email: string;
    phone: string;
    memberSince: string;
    memberLastYear: string;
  };

Before adding

  typescript: {
    typeCheck: true
  }

to my nuxt.config.ts, everything works fine.

But as soon as I add the typeCheck: true, the compilation results in 9 errors, all related to the same thing which is

ERROR(vue-tsc) 'props.person' is possibly 'undefined'.

and that are in relation with all places where I use this props, mainly in this

if (props.mode === "update") {
  state.id = props.person.id;
  state.firstName = props.person.firstName;
  state.lastName = props.person.lastName;
  state.email = props.person.email;
  state.phone = props.person.phone;
  state.memberSince = props.person.memberSince.substring(0, 10);
  state.memberLastYear = props.person.memberLastYear;
}

I have been searching the internet for hours never finding a solution. I need some help.

1

There are 1 best solutions below

0
On

Finally providing a default value like this solved the problem

const props = defineProps({ mode: String,
    person: { type: Object as () => IPerson, 
    default:  {id:undefined,firstName:'',lastName:'',email:'',phone:'',memberSince:'',memberLastYear:''} }, });