Jesse de Wit
Jesse de Wit

Reputation: 4177

Vue: Typed props interface with optional properties

I made a method I want to be available on all my Vue instances, which I can call in case of an error and render a specific error component. Something like vue-error-page. I'm using typescript and now I want to make sure that the component is called with the right typed props, to get a compile time error if there is a typo in the passed props.

Currently I have this in a shims.d.ts file:

import Vue, {VueConstructor} from 'vue'

declare module 'vue/types/vue' {
    interface Vue {
        $error (component:VueConstructor<Vue>, props:unknown) : void;
    }
}

Which allows me to call my plugin like so. The passed object matches the props definition of the ErrorPage component:

import Vue from "vue";
import ErrorPage from "./views/ErrorPage.vue";
export default Vue.extend({
  mounted() {
    this.$error(ErrorPage, { errorTitle: "Could not load the page" });
  }
});

While this is working, I want to get a compile-time error if the props don't match what the component expects.

So I thought I'd change the shims.d.ts like this:

declare module 'vue/types/vue' {
    interface Vue {
        $error<Props> (component:ExtendedVue<Vue, unknown, unknown, unknown, Props>, props:Props) : void;
    }
}

Now I get compile time errors when the props object doesn't match. However, I also get errors when I do not pass in optional properties. My ErrorPage component looks like this:

import Vue from "vue";
export default Vue.extend({
  name: "Error",
  props: {
    errorTitle: {
      type: String,
      required: true,
      default: "Error"
    },
    errorDescription: {
      type: String,
      required: false,
      default:
        "Something went wrong."
    }
  }
});

If I do not pass errorDescription, I should not get an error. That is what I am trying to accomplish. I want to be able to do the following things:

// Should compile and does right now.
this.$error(ErrorPage, {errorTitle: "Oops", errorDescription: "Something went wrong" });

// Should compile and does not right now. Fails with error:
// Argument of type '{ errorTitle: string; }' is not assignable to parameter of type '{ errorTitle: string; errorDescription: string; }'.
// Property 'errorDescription' is missing in type '{ errorTitle: string; }' but required in type '{ errorTitle: string; errorDescription: string; }'."
this.$error(ErrorPage, {errorTitle: "Oops" });

TL;DR Question:

How can I make my method call type safe with props as an argument, while being able to omit optional properties? I am able to change both the shims.d.ts and the ErrorPage component.

Upvotes: 3

Views: 3432

Answers (1)

Radu Diță
Radu Diță

Reputation: 14171

Even though you flag it as not required, the typescript type is still String and as such you need to pass it when calling the method.

You may try this (I haven't tested if it works)

Declare a type that is nullable, using an alias

type ErrorDescriptionType = String | undefined | null

And then pass this as the type for errorDescription

errorDescription: {
  type: Object as () => ErrorDescriptionType,
}

Upvotes: 1

Related Questions