TypeScript z Options API
Ta strona zakłada, że przeczytałeś już Używanie Vue z TypeScript.
TIP
Mimo że Vue wspiera użycie TypeScript z Options API, zalecane jest używanie Vue z TypeScript poprzez Composition API, ponieważ oferuje prostsze, wydajniejsze i bardziej niezawodne wnioskowanie typów.
Typowanie propsów komponentu
Wnioskowanie typów dla propsów w Options API wymaga owinięcia komponentu w defineComponent()
. Dzięki temu Vue jest w stanie wywnioskować typy dla propsów na podstawie opcji props
, biorąc pod uwagę dodatkowe opcje takie jak required: true
i default
:
ts
import { defineComponent } from 'vue'
export default defineComponent({
// wnioskowanie typów włączone
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
}
})
Jednakże opcje props
wykonywane w czasie działania wspierają jedynie użycie funkcji konstruktora jako typ propa - nie ma możliwości określenia złożonych typów, takich jak obiekty z zagnieżdżonymi właściwościami lub sygnaturami wywołania funkcji.
Aby opisać złożone typy propsów, możemy użyć typu pomocniczego PropType
:
ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
}
export default defineComponent({
props: {
book: {
// podaj bardziej szczegółowy typ dla `Object`
type: Object as PropType<Book>,
required: true
},
// można również opisać funkcje
callback: Function as PropType<(id: number) => void>
},
mounted() {
this.book.title // string
this.book.year // number
// Błąd TS: argument typu 'string' nie jest
// przypisywalny do parametru typu 'number'
this.callback?.('123')
}
})
Zastrzeżenia
Jeśli używasz wersji TypeScript niższej niż 4.7
, musisz zachować ostrożność używając wartości funkcji dla opcji propów validator
i default
- upewnij się, że używasz funkcji strzałkowych:
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>,
// Upewnij się, że używasz funkcji strzałkowych jeśli Twoja wersja TypeScript jest niższa niż 4.7
default: () => ({
title: 'Wyrażenie Funkcji Strzałkowej'
}),
validator: (book: Book) => !!book.title
}
}
})
Zapobiega to konieczności wnioskowania przez TypeScript typu this
wewnątrz tych funkcji, co niestety może powodować niepowodzenie wnioskowania typów. Było to wcześniejsze ograniczenie projektowe, które zostało ulepszone w TypeScript 4.7.
Typowanie emitów komponentu
Możemy zadeklarować oczekiwany typ payload'u dla emitowanego zdarzenia używając składni obiektowej opcji emits
. Dodatkowo, wszystkie niezadeklarowane emitowane zdarzenia będą powodować błąd typu podczas wywoływania:
ts
import { defineComponent } from 'vue'
export default defineComponent({
emits: {
addBook(payload: { bookName: string }) {
// wykonaj walidację w czasie wykonania
return payload.bookName.length > 0
}
},
methods: {
onSubmit() {
this.$emit('addBook', {
bookName: 123 // Błąd typu!
})
this.$emit('non-declared-event') // Błąd typu!
}
}
})
Typowanie właściwości obliczanych
Właściwość obliczana wywnioskuje swój typ na podstawie zwracanej wartości:
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Witaj!'
}
},
computed: {
greeting() {
return this.message + '!'
}
},
mounted() {
this.greeting // typ: string
}
})
W niektórych przypadkach możesz chcieć jawnie opisać typ właściwości obliczanej, aby upewnić się, że jej implementacja jest poprawna:
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Witaj!'
}
},
computed: {
// jawnie opisz typ zwracany
greeting(): string {
return this.message + '!'
},
// opisywanie zapisywalnej właściwości obliczanej
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
},
set(newValue: string) {
this.message = newValue.toUpperCase()
}
}
}
})
Jawne adnotacje mogą być również wymagane w niektórych skrajnych przypadkach, gdy TypeScript nie może wywnioskować typu właściwości obliczanej z powodu cyklicznych pętli wnioskowania.
Typowanie Obsługi Zdarzeń
Podczas pracy z natywnymi zdarzeniami DOM, może być przydatne poprawne typowanie argumentu, który przekazujemy do funkcji obsługującej. Przyjrzyjmy się temu przykładowi:
vue
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event) {
// `event` niejawnie ma typ `any`
console.log(event.target.value)
}
}
})
</script>
<template>
<input type="text" @change="handleChange" />
</template>
Bez adnotacji typu, argument event
będzie miał niejawnie typ any
. Spowoduje to błąd TS jeśli "strict": true
lub "noImplicitAny": true
są używane w tsconfig.json
. Dlatego zaleca się jawne oznaczenie argumentu handlera zdarzeń. Dodatkowo może być konieczne użycie asercji typu podczas uzyskiwania dostępu do właściwości event
:
ts
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})
Rozszerzanie Właściwości Globalnych
Niektóre wtyczki instalują globalnie dostępne właściwości dla wszystkich instancji komponentów poprzez app.config.globalProperties
. Na przykład, możemy zainstalować this.$http
do pobierania danych lub this.$translate
do internacjonalizacji. Aby działało to poprawnie z TypeScript, Vue udostępnia interfejs ComponentCustomProperties
zaprojektowany do rozszerzania poprzez rozszerzanie modułów TypeScript:
ts
import axios from 'axios'
declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
}
}
Zobacz także:
Umiejscowienie Rozszerzenia Typów
Możemy umieścić to rozszerzenie typów w pliku .ts
lub w ogólnoprojektowym pliku *.d.ts
. W obu przypadkach upewnij się, że jest ono uwzględnione w tsconfig.json
. Dla autorów bibliotek/wtyczek, ten plik powinien być określony we właściwości types
w package.json
.
Aby skorzystać z rozszerzenia modułu, musisz upewnić się, że rozszerzenie jest umieszczone w module TypeScript. Oznacza to, że plik musi zawierać co najmniej jeden import
lub export
na najwyższym poziomie, nawet jeśli jest to tylko export {}
. Jeśli rozszerzenie zostanie umieszczone poza modułem, nadpisze oryginalne typy zamiast je rozszerzać!
ts
// Nie działa, nadpisuje oryginalne typy.
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}
ts
// Działa poprawnie
export {}
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}
Rozszerzanie Opcji Niestandardowych
Niektóre wtyczki, na przykład vue-router
, zapewniają wsparcie dla niestandardowych opcji komponentów, takich jak beforeRouteEnter
:
ts
import { defineComponent } from 'vue'
export default defineComponent({
beforeRouteEnter(to, from, next) {
// ...
}
})
Bez odpowiedniego rozszerzenia typów, argumenty tego hooka będą miały niejawnie typ any
. Możemy rozszerzyć interfejs ComponentCustomOptions
, aby obsługiwał te niestandardowe opcje:
ts
import { Route } from 'vue-router'
declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(to: Route, from: Route, next: () => void): void
}
}
Teraz opcja beforeRouteEnter
będzie miała właściwe typy. Zauważ, że to tylko przykład - dobrze typowane biblioteki jak vue-router
powinny automatycznie wykonywać te rozszerzenia w swoich własnych definicjach typów.
Umiejscowienie tego rozszerzenia podlega tym samym ograniczeniom co rozszerzenia właściwości globalnych.
Zobacz także: