Vue CLI 提供内置的 TypeScript 工具支持。
#NPM 包中的官方声明
随着应用的增长,静态类型系统可以帮助防止许多潜在的运行时错误,这就是为什么 Vue 3 是用 TypeScript 编写的。这意味着在 Vue 中使用 TypeScript 不需要任何其他工具——它具有一流的公民支持。
#推荐配置
1. // tsconfig.json
2. {
3. "compilerOptions": {
4. "target": "esnext",
5. "module": "esnext",
6. // 这样就可以对 `this` 上的数据属性进行更严格的推断`
7. "strict": true,
8. "jsx": "preserve",
9. "moduleResolution": "node"
10. }
11. }
请注意,必须包含 strict: true
(或至少包含 noImplicitThis: true
,它是 strict
标志的一部分) 才能在组件方法中利用 this
的类型检查,否则它总是被视为 any
类型。
#开发工具
#项目创建
Vue CLI 可以生成使用 TypeScript 的新项目,开始:
1. # 1. Install Vue CLI, 如果尚未安装
2. npm install --global @vue/cli@next
3.
4.
5. # 2. 创建一个新项目, 选择 "Manually select features" 选项
6. vue create my-project-name
7.
8.
9. # 3. 如果已经有一个不存在TypeScript的 Vue CLI项目,请添加适当的 Vue CLI插件:
10. vue add typescript
请确保组件的 script
部分已将语言设置为 TypeScript:
1. <script lang="ts">
2. ...
3. </script>
#编辑器支持
对于使用 TypeScript 开发 Vue 应用程序,我们强烈建议使用 Visual Studio Code,它为 TypeScript 提供了很好的开箱即用支持。如果你使用的是单文件组件 (SFCs),那么可以使用很棒的 Vetur extension,它在 SFCs 中提供了 TypeScript 推理和许多其他优秀的特性。
WebStorm 还为 TypeScript 和 Vue 提供现成的支持。
#定义 Vue 组件
要让 TypeScript 正确推断 Vue 组件选项中的类型,需要使用 defineComponent
全局方法定义组件:
1. import { defineComponent } from 'vue'
2.
3.
4. const Component = defineComponent({
5. // 已启用类型推断
6. })
#与 Options API 一起使用
TypeScript 应该能够在不显式定义类型的情况下推断大多数类型。例如,如果有一个具有数字 count
property 的组件,如果试图对其调用特定于字符串的方法,则会出现错误:
1. const Component = defineComponent({
2. data() {
3. return {
4. count: 0
5. }
6. },
7. mounted() {
8. const result = this.count.split('') // => Property 'split' does not exist on type 'number'
9. }
10. })
如果你有一个复杂的类型或接口,你可以使用 type assertion 对其进行强制转换:
1. interface Book {
2. title: string
3. author: string
4. year: number
5. }
6.
7.
8. const Component = defineComponent({
9. data() {
10. return {
11. book: {
12. title: 'Vue 3 Guide',
13. author: 'Vue Team',
14. year: 2020
15. } as Book
16. }
17. }
18. })
#注释返回类型
由于 Vue 声明文件的循环特性,TypeScript 可能难以推断 computed 的类型。因此,你可能需要注释返回类型的计算属性。
1. import { defineComponent } from 'vue'
2.
3.
4. const Component = defineComponent({
5. data() {
6. return {
7. message: 'Hello!'
8. }
9. },
10. computed: {
11. // 需要注释
12. greeting(): string {
13. return this.message + '!'
14. }
15.
16.
17. // 在使用setter进行计算时,需要对getter进行注释
18. greetingUppercased: {
19. get(): string {
20. return this.greeting.toUpperCase();
21. },
22. set(newValue: string) {
23. this.message = newValue.toUpperCase();
24. },
25. },
26. }
27. })
#注释 Props
Vue 对定义了 type
的 prop 执行运行时验证。要将这些类型提供给 TypeScript,我们需要使用 PropType
强制转换构造函数:
1. import { defineComponent, PropType } from 'vue'
2.
3.
4. interface ComplexMessage {
5. title: string
6. okMessage: string
7. cancelMessage: string
8. }
9. const Component = defineComponent({
10. props: {
11. name: String,
12. success: { type: String },
13. callback: {
14. type: Function as PropType<() => void>
15. },
16. message: {
17. type: Object as PropType<ComplexMessage>,
18. required: true,
19. validator(message: ComplexMessage) {
20. return !!message.title
21. }
22. }
23. }
24. })
如果你发现验证器没有得到类型推断或者成员完成不起作用,那么用期望的类型注释参数可能有助于解决这些问题。
#与组合式 API 一起使用
在 setup()
函数中,不需要将类型传递给 props
参数,因为它将从 props
组件选项推断类型。
1. import { defineComponent } from 'vue'
2.
3.
4. const Component = defineComponent({
5. props: {
6. message: {
7. type: String,
8. required: true
9. }
10. },
11.
12.
13. setup(props) {
14. const result = props.message.split('') // 正确, 'message' 被声明为字符串
15. const filtered = props.message.filter(p => p.value) // 将引发错误: Property 'filter' does not exist on type 'string'
16. }
17. })
#类型声明 ref
Refs 根据初始值推断类型:
1. import { defineComponent, ref } from 'vue'
2.
3.
4. const Component = defineComponent({
5. setup() {
6. const year = ref(2020)
7.
8.
9. const result = year.value.split('') // => Property 'split' does not exist on type 'number'
10. }
11. })
有时我们可能需要为 ref 的内部值指定复杂类型。我们可以在调用 ref 重写默认推理时简单地传递一个泛型参数:
1. const year = ref<string | number>('2020') // year's type: Ref<string | number>
2.
3.
4. year.value = 2020 // ok!
TIP
如果泛型的类型未知,建议将 ref
转换为 Ref<T>
。
#类型声明 reactive
当声明类型 reactive
property,我们可以使用接口:
1. import { defineComponent, reactive } from 'vue'
2.
3.
4. interface Book {
5. title: string
6. year?: number
7. }
8.
9.
10. export default defineComponent({
11. name: 'HelloWorld',
12. setup() {
13. const book = reactive<Book>({ title: 'Vue 3 Guide' })
14. // or
15. const book: Book = reactive({ title: 'Vue 3 Guide' })
16. // or
17. const book = reactive({ title: 'Vue 3 Guide' }) as Book
18. }
19. })
#类型声明 computed
计算值将根据返回值自动推断类型
1. import { defineComponent, ref, computed } from 'vue'
2.
3.
4. export default defineComponent({
5. name: 'CounterButton',
6. setup() {
7. let count = ref(0)
8.
9.
10. // 只读
11. const doubleCount = computed(() => count.value * 2)
12.
13.
14. const result = doubleCount.value.split('') // => Property 'split' does not exist on type 'number'
15. }
16. })