Vue 3 + TS

Vue 3 adds really solid Typescript support. One place where it can get a little tricky, is typing the props we define for passing data into our components. Vue offers a "native js" way of doing this... but there are obviously limitations to this given the dynamic type system in js. Below are some patterns I've found for making prop types much more Typescript friendly. Enjoy!

PropType

import { PropType } from 'vue'

PropType is a utility type provided by vue that allows you to effectively type a prop on your component by passing PropType your type of choice. Here's an example.

<template>
  <div>
    {{ label }}
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'

export default defineComponent({
  name: 'CoolLabelComponent',
  props: {
    label: {
      type: String as PropType<string>,
      default: 'Label',
    },
  },
})
</script>

Use with Care

Notice in our prop definition for label we force cast type: String as PropType<string> . Make sure you're aware of what's going on here.... by using the as PropType<string> we force Typescript to interpret whatever value is passed into our prop as a string type, so you'll want to be confident that that prop is actually a string.

Handling Defaults

It's especially important to think about the as syntax when providing a default value for your prop. In non-typescript vue code something like

props: {
    label: {
      type: String,
      default: null,
    },
  },

is totally valid. We're saying that our label prop is a String but by default it starts as null. This is a pretty sane pattern, considering that many times prop data might get populated asynchronously or be optional.

To make sure we handle our null default, we can use a union type. Then handle the potential null value whenever we reference our prop. Here's what that looks like:

<template>
  <div>
    {{ lowerLabel }}
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'

export default defineComponent({
  name: 'CoolLabelComponent',
  props: {
    label: {
      type: String as PropType<string | null>,
      default: null,
    },
  },
	computed: {
    lowerLabel () {
      return this.label ? this.label.toLowerCase() : 'label is null 👀'
    }
  }
})
</script>