Skip to content

TypeScript s Options API

Tato stránka předpokládá, že jste již přečetli přehled o Používání Vue s TypeScriptem.

TIP

I když Vue používání TypeScriptu s Options API podporuje, doporučuje se používat Vue s TypeScriptem pomocí Composition API, protože nabízí jednodušší, efektivnější a robustnější odvozování typů.

Typování vlastností komponenty

Odvozování typů pro vlastnosti (props) v Options API vyžaduje obalení komponenty pomocí defineComponent(). Tímto způsobem je Vue schopno odvodit typy pro vlastnosti na základě možnosti props, přičemž bere v úvahu další vlastnosti, jako je required: true a default:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  // povolené odvozování typů
  props: {
    name: String,
    id: [Number, String],
    msg: { type: String, required: true },
    metadata: null
  },
  mounted() {
    this.name // typ: string | undefined
    this.id // typ: number | string | undefined
    this.msg // typ: string
    this.metadata // typ: any
  }
})

Za běhu však props podporují pro typ vlastnosti pouze použití konstruktorových funkcí - není možné specifikovat složité typy, jako jsou objekty s vnořenými vlastnostmi nebo signatury volání funkcí.

Pro anotaci složitých typů vlastností můžeme použít utility třídu PropType:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  author: string
  year: number
}

export default defineComponent({
  props: {
    book: {
      // poskytne konkrétnější typ pro `Object`
      type: Object as PropType<Book>,
      required: true
    },
    // lze také anotovat funkce
    callback: Function as PropType<(id: number) => void>
  },
  mounted() {
    this.book.title // string
    this.book.year // number

    // TS chyba: parametr typu 'string' nemůže
    // být přiřazen do parametru typu 'number'
    this.callback?.('123')
  }
})

Omezení

Pokud máte verzi TypeScriptu nižší než 4.7, musíte být opatrní při používání funkčních hodnot pro volby vlastností validator a default - ujistěte se, že používáte arrow funkce:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  year?: number
}

export default defineComponent({
  props: {
    bookA: {
      type: Object as PropType<Book>,
      // ujistěte se, že používáte arrow funkce, 
      // pokud máte verzi TypeScriptu nižší než 4.7
      default: () => ({
        title: 'Výraz arrow funkce'
      }),
      validator: (book: Book) => !!book.title
    }
  }
})

Tím se zabrání TypeScriptu v odvozování typu this uvnitř těchto funkcí, což bohužel může způsobit selhání odvozování typu. Jednalo se o předchozí omezení návrhu, které bylo vylepšeno v TypeScript v4.7.

Typování emitovaných událostí komponenty

Můžeme deklarovat očekávaný typ dat pro emitovanou událost pomocí objektové syntaxe volby emits. Navíc všechny ne-deklarované emitované události vyvolají typovou chybu při volání:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  emits: {
    addBook(payload: { bookName: string }) {
      // provést runtime validaci
      return payload.bookName.length > 0
    }
  },
  methods: {
    onSubmit() {
      this.$emit('addBook', {
        bookName: 123 // Chyba typu!
      })

      this.$emit('nedeklarovana-udalost') // Chyba typu!
    }
  }
})

Typování computed proměnných

Computed proměnná odvozuje svůj typ na základě návratové hodnoty:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Ahoj!'
    }
  },
  computed: {
    greeting() {
      return this.message + '!'
    }
  },
  mounted() {
    this.greeting // typ: string
  }
})

V některých případech můžete chtít typ computed proměnné explicitně označit, abyste zajistili její správnou implementaci:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Ahoj!'
    }
  },
  computed: {
    // explicitně anotovaný návratový typ
    greeting(): string {
      return this.message + '!'
    },

    // anotace zapisovatelné computed proměnné
    greetingUppercased: {
      get(): string {
        return this.greeting.toUpperCase()
      },
      set(newValue: string) {
        this.message = newValue.toUpperCase()
      }
    }
  }
})

Explicitní označení může být také vyžadováno v některých okrajových případech, kdy TypeScript nedokáže typ computed proměnné odvodit kvůli cyklické inferenci.

Typování event handlerů

Při práci s nativními DOM událostmi může být užitečné správně označit argument, který předáváme obslužnému handleru. Podívejme se na tento příklad:

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

export default defineComponent({
  methods: {
    handleChange(event) {
      // `event` má implicitně typ `any`
      console.log(event.target.value)
    }
  }
})
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

Bez typového označení bude mít argument event implicitně typ any. To povede k chybě v TS, pokud je v tsconfig.json použita volba "strict": true nebo "noImplicitAny": true. Proto se doporučuje argumenty event handlerů explicitně označit. Kromě toho můžete potřebovat odvození typů při přístupu k vlastnostem objektu event:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  methods: {
    handleChange(event: Event) {
      console.log((event.target as HTMLInputElement).value)
    }
  }
})

Obohacování globálních vlastností

Některé pluginy instalují globálně dostupné vlastnosti do všech instancí komponent pomocí app.config.globalProperties. Například můžeme nainstalovat this.$http pro načítání dat nebo this.$translate pro internacionalizaci (překlad). Aby to dobře fungovalo s TypeScriptem, Vue poskytuje rozhraní ComponentCustomProperties, které je navrženo pro rozšíření pomocí obohacování (augmentation) TypeScript modulů:

ts
import axios from 'axios'

declare module 'vue' {
  interface ComponentCustomProperties {
    $http: typeof axios
    $translate: (key: string) => string
  }
}

Viz také:

Umístění obohacení typu

Obohacení typu můžeme umístit do souboru .ts nebo do souboru *.d.ts pro celý projekt. V obou případech se ujistěte, že je zahrnuto v souboru tsconfig.json. Pro autory knihoven / pluginů by měl být tento soubor specifikován vlastností types v souboru package.json.

Abychom mohli obohacení modulu využít, je nutné zajistit, aby bylo umístěno v TypeScript modulu. To znamená, že soubor musí obsahovat alespoň jeden import nebo export nejvyšší úrovně, i když je to jen export {}. Pokud je obohacení umístěno mimo modul, původní typy místo jejich doplnění přepíše!

ts
// Nefunguje, přepisuje původní typy.
declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}
ts
// Funguje správně
export {}

declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}

Rozšiřování custom možností

Některé pluginy, například vue-router, poskytují podporu pro vlastní možnosti komponent, jako je beforeRouteEnter:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  beforeRouteEnter(to, from, next) {
    // ...
  }
})

Bez správného obohacení typu budou mít parametry tohoto hooku implicitně typ any. Můžeme rozšířit rozhraní ComponentCustomOptions, abychom tyto vlastní možnosti podporovali:

ts
import { Route } from 'vue-router'

declare module 'vue' {
  interface ComponentCustomOptions {
    beforeRouteEnter?(to: Route, from: Route, next: () => void): void
  }
}

Nyní bude možnost beforeRouteEnter správně typována. Berte na vědomí, že se jedná pouze o příklad - dobře typované knihovny jako vue-router by měly tato obohacení ve vlastních definicích typů provádět automaticky.

Umístění tohoto obohacení podléhá stejným omezením jako obohacování globálních vlastností.

Viz také:

TypeScript s Options API has loaded