外观
使用选项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
这样的类型良好的库应该在其自己的类型定义中自动执行这些增强。
这种增强的位置受到与全局属性增强相同的限制。
另请参阅