外觀
使用選項API的TypeScript
此頁面假設您已經閱讀了使用TypeScript與Vue的概述。
提示
儘管Vue支援使用選項API的TypeScript使用,但建議透過組合式API使用Vue,因為它提供更簡單、更高效、更穩健的型別推斷。
編寫元件屬性型別
在選項API中,對props的型別推斷需要使用defineComponent()
包裝元件。透過它,Vue能夠根據props
選項推斷props的型別,並考慮額外的選項,如required: true
和default
ts
import { defineComponent } from 'vue'
export default defineComponent({
// type inference enabled
props: {
name: String,
id: [Number, String],
msg: { type: String, required: true },
metadata: null
},
mounted() {
this.name // type: string | undefined
this.id // type: number | string | undefined
this.msg // type: string
this.metadata // type: any
}
})
但是,執行時props
選項僅支援使用建構函式作為prop的型別 - 無法指定複雜型別,如具有巢狀屬性的物件或函式呼叫簽名。
為了註釋複雜props型別,我們可以使用PropType
實用型別
ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
}
export default defineComponent({
props: {
book: {
// provide more specific type to `Object`
type: Object as PropType<Book>,
required: true
},
// can also annotate functions
callback: Function as PropType<(id: number) => void>
},
mounted() {
this.book.title // string
this.book.year // number
// TS Error: argument of type 'string' is not
// assignable to parameter of type 'number'
this.callback?.('123')
}
})
注意事項
如果您的TypeScript版本低於4.7
,在使用函式值作為validator
和default
prop選項時必須小心 - 確保使用箭頭函式
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>,
// Make sure to use arrow functions if your TypeScript version is less than 4.7
default: () => ({
title: 'Arrow Function Expression'
}),
validator: (book: Book) => !!book.title
}
}
})
這可以防止TypeScript在函式內部推斷this
的型別,不幸的是,這可能導致型別推斷失敗。這曾經是一個先前的設計限制,現在已經在TypeScript 4.7中得到了改進。
Component Emits 型別
我們可以使用emits
選項的物件語法來宣告預期的事件負載型別。此外,所有未宣告的 emitted 事件在呼叫時都會丟擲型別錯誤。
ts
import { defineComponent } from 'vue'
export default defineComponent({
emits: {
addBook(payload: { bookName: string }) {
// perform runtime validation
return payload.bookName.length > 0
}
},
methods: {
onSubmit() {
this.$emit('addBook', {
bookName: 123 // Type error!
})
this.$emit('non-declared-event') // Type error!
}
}
})
Computed Properties 型別
計算屬性根據其返回值推斷型別。
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
greeting() {
return this.message + '!'
}
},
mounted() {
this.greeting // type: string
}
})
在某些情況下,您可能需要顯式註解計算屬性的型別,以確保其實現正確。
ts
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
// explicitly annotate return type
greeting(): string {
return this.message + '!'
},
// annotating a writable computed property
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
},
set(newValue: string) {
this.message = newValue.toUpperCase()
}
}
}
})
在某些邊緣情況下,可能也需要顯式註解,因為TypeScript由於迴圈推斷迴圈而無法推斷計算屬性的型別。
Event Handlers 型別
當處理原生的DOM事件時,正確地為傳遞給處理器的引數進行型別註解可能很有用。讓我們看看這個例子:
vue
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event) {
// `event` implicitly has `any` type
console.log(event.target.value)
}
}
})
</script>
<template>
<input type="text" @change="handleChange" />
</template>
沒有型別註解,event
引數將隱式地具有型別any
。如果使用"strict": true
或"noImplicitAny": true
,則這將在tsconfig.json
中導致TS錯誤。因此,建議顯式註解事件處理程式的引數。此外,在訪問event
的屬性時,您可能需要使用型別斷言。
ts
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})
全域性屬性增強
一些外掛透過app.config.globalProperties
將全域性可用的屬性安裝到所有元件例項中。例如,我們可能安裝this.$http
進行資料獲取或this.$translate
進行國際化。為了使TypeScript與之很好地協同工作,Vue公開了一個ComponentCustomProperties
介面,該介面設計用於透過TypeScript模組增強進行增強。
ts
import axios from 'axios'
declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
}
}
另請參閱
型別增強放置
我們可以將此型別增強放在一個.ts
檔案中,或者在一個專案範圍的*.d.ts
檔案中。無論如何,請確保它包含在tsconfig.json
中。對於庫/外掛作者,此檔案應在package.json
的types
屬性中指定。
為了利用模組增強,您需要確保增強放置在一個TypeScript模組中。也就是說,檔案需要至少包含一個頂層import
或export
,即使它只是export {}
。如果增強放置在模組之外,它將覆蓋原始型別而不是增強它們!
ts
// Does not work, overwrites the original types.
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}
ts
// Works correctly
export {}
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}
自定義選項增強
一些外掛,例如vue-router
,為自定義元件選項(如beforeRouteEnter
)提供支援。
ts
import { defineComponent } from 'vue'
export default defineComponent({
beforeRouteEnter(to, from, next) {
// ...
}
})
如果沒有適當型別增強,此鉤子的引數將隱式具有型別any
。我們可以增強ComponentCustomOptions
介面以支援這些自定義選項。
ts
import { Route } from 'vue-router'
declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(to: Route, from: Route, next: () => void): void
}
}
現在 beforeRouteEnter
選項將得到正確的型別。請注意,這只是一個示例——像 vue-router
這樣的型別良好的庫應該在其自己的型別定義中自動執行這些增強。
這種增強的位置受到與全域性屬性增強相同的限制。
另請參閱