TypeScript 支持

Vue CLITypeScript 支持 - 图1 (opens new window) 提供内置的 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 类型。

参见 TypeScript 编译选项文档TypeScript 支持 - 图2 (opens new window)查看更多细节。

开发工具

项目创建

Vue CLITypeScript 支持 - 图3 (opens new window) 可以生成使用 TypeScript 的新项目,开始:

  1. # 1. Install Vue CLI, 如果尚未安装
  2. npm install --global @vue/cli@next
  3. # 2. 创建一个新项目, 选择 "Manually select features" 选项
  4. vue create my-project-name
  5. # 3. 如果已经有一个不存在TypeScript的 Vue CLI项目,请添加适当的 Vue CLI插件:
  6. vue add typescript

请确保组件的 script 部分已将语言设置为 TypeScript:

  1. <script lang="ts">
  2. ...
  3. </script>

编辑器支持

对于使用 TypeScript 开发 Vue 应用程序,我们强烈建议使用 Visual Studio CodeTypeScript 支持 - 图4 (opens new window),它为 TypeScript 提供了很好的开箱即用支持。如果你使用的是单文件组件 (SFCs),那么可以使用很棒的 Vetur extensionTypeScript 支持 - 图5 (opens new window),它在 SFCs 中提供了 TypeScript 推理和许多其他优秀的特性。

WebStormTypeScript 支持 - 图6 (opens new window) 还为 TypeScript 和 Vue 提供现成的支持。

定义 Vue 组件

要让 TypeScript 正确推断 Vue 组件选项中的类型,需要使用 defineComponent 全局方法定义组件:

  1. import { defineComponent } from 'vue'
  2. const Component = defineComponent({
  3. // 已启用类型推断
  4. })

与 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 assertionTypeScript 支持 - 图7 (opens new window) 对其进行强制转换:

  1. interface Book {
  2. title: string
  3. author: string
  4. year: number
  5. }
  6. const Component = defineComponent({
  7. data() {
  8. return {
  9. book: {
  10. title: 'Vue 3 Guide',
  11. author: 'Vue Team',
  12. year: 2020
  13. } as Book
  14. }
  15. }
  16. })

注释返回类型

由于 Vue 声明文件的循环特性,TypeScript 可能难以推断 computed 的类型。因此,你可能需要注释返回类型的计算属性。

  1. import { defineComponent } from 'vue'
  2. const Component = defineComponent({
  3. data() {
  4. return {
  5. message: 'Hello!'
  6. }
  7. },
  8. computed: {
  9. // 需要注释
  10. greeting(): string {
  11. return this.message + '!'
  12. }
  13. // 在使用setter进行计算时,需要对getter进行注释
  14. greetingUppercased: {
  15. get(): string {
  16. return this.greeting.toUpperCase();
  17. },
  18. set(newValue: string) {
  19. this.message = newValue.toUpperCase();
  20. },
  21. },
  22. }
  23. })

注释 Props

Vue 对定义了 type 的 prop 执行运行时验证。要将这些类型提供给 TypeScript,我们需要使用 PropType 强制转换构造函数:

  1. import { defineComponent, PropType } from 'vue'
  2. interface ComplexMessage {
  3. title: string
  4. okMessage: string
  5. cancelMessage: string
  6. }
  7. const Component = defineComponent({
  8. props: {
  9. name: String,
  10. success: { type: String },
  11. callback: {
  12. type: Function as PropType<() => void>
  13. },
  14. message: {
  15. type: Object as PropType<ComplexMessage>,
  16. required: true,
  17. validator(message: ComplexMessage) {
  18. return !!message.title
  19. }
  20. }
  21. }
  22. })

如果你发现验证器没有得到类型推断或者成员完成不起作用,那么用期望的类型注释参数可能有助于解决这些问题。

与 Composition API 一起使用

setup() 函数中,不需要将类型传递给 props 参数,因为它将从 props 组件选项推断类型。

  1. import { defineComponent } from 'vue'
  2. const Component = defineComponent({
  3. props: {
  4. message: {
  5. type: String,
  6. required: true
  7. }
  8. },
  9. setup(props) {
  10. const result = props.message.split('') // 正确, 'message' 被声明为字符串
  11. const filtered = props.message.filter(p => p.value) // 将引发错误: Property 'filter' does not exist on type 'string'
  12. }
  13. })

类型声明 ref

Refs 根据初始值推断类型:

  1. import { defineComponent, ref } from 'vue'
  2. const Component = defineComponent({
  3. setup() {
  4. const year = ref(2020)
  5. const result = year.value.split('') // => Property 'split' does not exist on type 'number'
  6. }
  7. })

有时我们可能需要为 ref 的内部值指定复杂类型。我们可以在调用 ref 重写默认推理时简单地传递一个泛型参数:

  1. const year = ref<string | number>('2020') // year's type: Ref<string | number>
  2. year.value = 2020 // ok!

TIP

如果泛型的类型未知,建议将 ref 转换为 Ref<T>

类型声明 reactive

当声明类型 reactive property,我们可以使用接口:

  1. import { defineComponent, reactive } from 'vue'
  2. interface Book {
  3. title: string
  4. year?: number
  5. }
  6. export default defineComponent({
  7. name: 'HelloWorld',
  8. setup() {
  9. const book = reactive<Book>({ title: 'Vue 3 Guide' })
  10. // or
  11. const book: Book = reactive({ title: 'Vue 3 Guide' })
  12. // or
  13. const book = reactive({ title: 'Vue 3 Guide' }) as Book
  14. }
  15. })

类型声明 computed

计算值将根据返回值自动推断类型

  1. import { defineComponent, ref, computed } from 'vue'
  2. export default defineComponent({
  3. name: 'CounterButton',
  4. setup() {
  5. let count = ref(0)
  6. // 只读
  7. const doubleCount = computed(() => count.value * 2)
  8. const result = doubleCount.value.split('') // => Property 'split' does not exist on type 'number'
  9. }
  10. })