跳轉到內容

使用選項API的TypeScript

此頁面假設您已經閱讀了使用TypeScript與Vue的概述。

提示

儘管Vue支援使用選項API的TypeScript使用,但建議透過組合式API使用Vue,因為它提供更簡單、更高效、更穩健的型別推斷。

編寫元件屬性型別

在選項API中,對props的型別推斷需要使用defineComponent()包裝元件。透過它,Vue能夠根據props選項推斷props的型別,並考慮額外的選項,如required: truedefault

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,在使用函式值作為validatordefault 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.jsontypes屬性中指定。

為了利用模組增強,您需要確保增強放置在一個TypeScript模組中。也就是說,檔案需要至少包含一個頂層importexport,即使它只是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 這樣的型別良好的庫應該在其自己的型別定義中自動執行這些增強。

這種增強的位置受到與全域性屬性增強相同的限制

另請參閱

使用選項API的TypeScript已載入