EP57 - 選項 API 中使用 TypeScript

閱讀時間約 16 分鐘
TypeScript with Options API, 上一篇組件API已經蠻頭大了
選項API雖然是舊版Vue的方法,但還是對整個框架的原理有幫助吧~
繼續來看看吧~

這篇文章假設您已經閱讀了《使用 TypeScript 的 Vue 概述》。

提示:雖然 Vue 支援使用 Options API 的 TypeScript,但建議透過 Composition API 使用 TypeScript,因為這樣可以提供更簡單、更高效和更穩健的類型推斷。

元件屬性類型標註 - Typing Component Props

在 Options API 中,對屬性的類型推斷需要使用 defineComponent() 將元件包裝起來。這樣一來,Vue 就能根據屬性選項推斷屬性的類型,並考慮其他選項,例如 required: truedefault

import { defineComponent } from 'vue'

export default defineComponent({
// 啟用類型推斷
props: {
name: String,
id: [Number, String],
msg: { type: String, required: true },
metadata: null
},
mounted() {
this.name // 類型:string | undefined
this.id // 類型:number | string | undefined
this.msg // 類型:string
this.metadata // 類型:any
}
})

然而,運行時的屬性選項僅支持使用構造函數作為屬性類型,無法指定複雜類型,例如具有嵌套屬性的物件或函數調用簽名。

要標註複雜的屬性類型,我們可以使用 PropType 實用類型:

import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
title: string
author: string
year: number
}

export default defineComponent({
props: {
book: {
// 提供更具體的 `Object` 類型
type: Object as PropType<Book>,
required: true
},
// 也可以標註函數
callback: Function as PropType<(id: number) => void>
},
mounted() {
this.book.title // string
this.book.year // number

// TS 錯誤:類型 'string' 的參數
// 無法分配給類型 'number' 的參數
this.callback?.('123')
}
})

注意事項

如果您的 TypeScript 版本低於 4.7,當使用函數值作為驗證器和預設屬性選項時,必須小心,確保使用箭頭函數:

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>,
// 如果您的 TypeScript 版本低於 4.7,請確保使用箭頭函數
default: () => ({
title: '箭頭函數表達式'
}),
validator: (book: Book) => !!book.title
}
}
})

這樣可以防止 TypeScript 在這些函數內部推斷 this 的類型,這不幸地會導致類型推斷失敗。這是之前的設計限制,現在在 TypeScript 4.7 中已經有所改善。

元件事件類型標註 - Typing Component Emits

我們可以使用 emits 選項的物件語法來宣告發射事件的預期有效載荷類型。此外,所有未聲明的發射事件在被呼叫時都會拋出類型錯誤:

import { defineComponent } from 'vue'

export default defineComponent({
emits: {
addBook(payload: { bookName: string }) {
// 執行運行時驗證
return payload.bookName.length > 0
}
},
methods: {
onSubmit() {
this.$emit('addBook', {
bookName: 123 // 類型錯誤!
})

this.$emit('non-declared-event') // 類型錯誤!
}
}
})

元件計算屬性類型標註 - Typing Computed Properties

計算屬性會根據其返回值推斷其類型:

import { defineComponent } from 'vue'

export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
greeting() {
return this.message + '!'
}
},
mounted() {
this.greeting // 類型: string
}
})

在某些情況下,您可能希望明確標註計算屬性的類型,以確保其實現是正確的:

import { defineComponent } from 'vue'

export default defineComponent({
data() {
return {
message: 'Hello!'
}
},
computed: {
// 明確標註返回類型
greeting(): string {
return this.message + '!'
},

// 標註可寫的計算屬性
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
},
set(newValue: string) {
this.message = newValue.toUpperCase()
}
}
}
})

在某些邊緣情況下,當 TypeScript 因循環推斷循環而無法推斷計算屬性的類型時,也可能需要明確的標註。

事件處理程序類型標註 - Typing Event Handlers

在處理原生 DOM 事件時,正確標註傳遞給處理程序的參數類型可能會很有用。讓我們看一下這個例子:

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
methods: {
handleChange(event) {
// `event` 隱式具有 `any` 類型
console.log(event.target.value)
}
}
})
</script>

<template>
<input type="text" @change="handleChange" />
</template>

如果沒有類型標註,事件參數將隱式具有 any 類型。如果在 tsconfig.json 中使用了 "strict": true"noImplicitAny": true,則會導致 TS 錯誤。因此,建議明確標註事件處理程序的參數。此外,當訪問事件的屬性時,您可能需要使用類型斷言:

import { defineComponent } from 'vue'

export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})

擴充全域屬性 - Augmenting Global Properties

一些插件通過 app.config.globalProperties 為所有組件實例安裝全域可用的屬性。例如,我們可以安裝 this.$http 用於數據獲取或 this.$translate 用於國際化。為了讓這與 TypeScript 相容,Vue 提供了 ComponentCustomProperties 接口,旨在通過 TypeScript 模組擴充來增強:

import axios from 'axios'

declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
}
}

參考:

類型擴充位置 - Type Augmentation Placement

我們可以將這種類型擴充放在 .ts 檔案中,或者放在全專案的 *.d.ts 檔案中。無論哪種方式,請確保它包含在 tsconfig.json 中。對於庫或插件作者,這個檔案應在 package.jsontypes 屬性中指定。

為了利用模組擴充,您需要確保擴充放置在 TypeScript 模組中。也就是說,該檔案需要包含至少一個頂層的 import 或 export,即使它只是 export {}。如果擴充放置在模組之外,它將覆蓋原始類型,而不是進行增強!

// 不工作,覆蓋原始類型。
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}
// 正常工作
export {}

declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
}
}

擴充自訂選項 - Augmenting Custom Options

一些插件,例如 vue-router,提供對自訂組件選項的支持,例如 beforeRouteEnter

import { defineComponent } from 'vue'

export default defineComponent({
beforeRouteEnter(to, from, next) {
// ...
}
})

如果沒有適當的類型擴充,這個鉤子的參數將隱式具有 any 類型。我們可以擴充 ComponentCustomOptions 接口來支持這些自訂選項:

import { Route } from 'vue-router'

declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(to: Route, from: Route, next: () => void): void
}
}

現在 beforeRouteEnter 選項將被正確類型化。請注意,這只是一個示例,像 vue-router 這樣的良好類型化庫應該會自動在其自己的類型定義中執行這些擴充。

這種擴充的放置遵循與全域屬性擴充相同的限制

參考:

Q: 所以組件api跟項目api在typescript差異?

在 TypeScript 中,組件 API(Component API)和項目 API(Project API)的差異主要在於它們的使用場景和型別定義方式。以下是一些關鍵的差異:

組件 API

  1. 定義範圍
    • 組件 API 是針對單個組件的接口,通常包括該組件的 props、emits、computed 屬性和方法。
  2. 型別推斷
    • 使用 defineComponent() 時,TypeScript 能夠自動推斷組件的屬性類型。例如,對於 props 的定義,TypeScript 能夠從 props 選項中推斷型別。
  3. 使用的主要範例
    • 在組件內部定義 props、emits、methods 和 lifecycle hooks 的型別化。

項目 API

  1. 定義範圍
    • 項目 API 是針對整個項目的接口,通常涉及到全局屬性、插件和整個應用程序的配置。
  2. 型別增強
    • 使用 TypeScript 的模組擴充(Module Augmentation)來增強 Vue 的全局屬性,如 app.config.globalProperties,以使其能與 TypeScript 進行協同工作。
  3. 使用的主要範例
    • 對全局屬性進行型別定義,例如添加 $http$translate 屬性,並使用 declare module 來增強 Vue 的內部類型。

總結

  • 組件 API 更加專注於組件的內部結構和類型推斷,適用於單個組件的開發;而 項目 API 則是針對整個應用程序的配置和全局屬性,通常需要透過模組擴充來進行型別定義。
  • 組件 API 通常使用 defineComponent(),而項目 API 通常需要使用 TypeScript 的模組擴充來增強 Vue 的全局類型。這使得開發者能夠在整個應用中使用這些自定義的全局屬性和方法。
快來洗洗睡覺吧~
多接觸TypeScript好像慢慢理解~
他就是對各種東西都給他Type!的Script啊www
avatar-img
2會員
70內容數
分享生活趣事~
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
卡關的人生 的其他內容
在使用 Vue 組件 API 和 TypeScript 的開發中,通常會遇到類型錯誤和需要 debug 的情況。組件 API 下的 TypeScript 提供了更強的型別檢查和類型推斷功能,這有助於減少錯誤。
使用 TypeScript 開發 Vue 的好處包括:靜態類型檢查可減少運行時錯誤,提升代碼重構的安全性。TypeScript 提供更好的開發體驗,因為 IDE 支持類型自動完成功能。
允許未經過濾的使用者內容執行會帶來攻擊風險。建議參考 HTML5 Security Cheat Sheet 和 OWASP's XSS Prevention Cheat Sheet,並檢查依賴項源代碼是否存在危險模式。
網站可及性(a11y)是指創建任何人都能使用的網站,無論是殘障人士、網速緩慢的用戶、使用過時或損壞硬體的人,還是處於不利環境中的人。例如,為視頻添加字幕能幫助聾人和聽力受損者,也能幫助處於嘈雜環境中的人。設計考慮應包括色彩對比、字體選擇、文本大小和語言。
頁面加載性能可透過PageSpeed Insights、WebPageTest等工具測量,而更新性能則需使用Chrome DevTools、Vue DevTools等進行分析。提升頁面加載性能的技術包括選擇合適架構(SSR或SSG)、(tree-shaking)、(code splitting)等。
在生產部署中,Vue 提供開發環境及生產環境的最佳實踐。開發階段有錯誤警告、驗證等功能,但生產環境須去除開發代碼以提升效能。未使用建構工具時,建議使用 .prod.js 生產版;使用工具如 Vite 或 Vue CLI 時,已預設生產配置,適合進行部署。
在使用 Vue 組件 API 和 TypeScript 的開發中,通常會遇到類型錯誤和需要 debug 的情況。組件 API 下的 TypeScript 提供了更強的型別檢查和類型推斷功能,這有助於減少錯誤。
使用 TypeScript 開發 Vue 的好處包括:靜態類型檢查可減少運行時錯誤,提升代碼重構的安全性。TypeScript 提供更好的開發體驗,因為 IDE 支持類型自動完成功能。
允許未經過濾的使用者內容執行會帶來攻擊風險。建議參考 HTML5 Security Cheat Sheet 和 OWASP's XSS Prevention Cheat Sheet,並檢查依賴項源代碼是否存在危險模式。
網站可及性(a11y)是指創建任何人都能使用的網站,無論是殘障人士、網速緩慢的用戶、使用過時或損壞硬體的人,還是處於不利環境中的人。例如,為視頻添加字幕能幫助聾人和聽力受損者,也能幫助處於嘈雜環境中的人。設計考慮應包括色彩對比、字體選擇、文本大小和語言。
頁面加載性能可透過PageSpeed Insights、WebPageTest等工具測量,而更新性能則需使用Chrome DevTools、Vue DevTools等進行分析。提升頁面加載性能的技術包括選擇合適架構(SSR或SSG)、(tree-shaking)、(code splitting)等。
在生產部署中,Vue 提供開發環境及生產環境的最佳實踐。開發階段有錯誤警告、驗證等功能,但生產環境須去除開發代碼以提升效能。未使用建構工具時,建議使用 .prod.js 生產版;使用工具如 Vite 或 Vue CLI 時,已預設生產配置,適合進行部署。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節旨在介紹TypeScript中的函數,包括其基本結構、如何呼叫函數、函數的參數以及函數的返回值等相關概念。通過本章節,讀者可以學習到如何在TypeScript中使用不同的方式來定義函數,如函數聲明、函數表達式、箭頭函數和匿名函數等。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
TypeScript是一種由Microsoft開發和維護的開源編程語言。它是JavaScript的超集,主要擴展了JavaScript的語法,增加了靜態類型檢查和其他特性,使得開發大型應用程序更為方便和可靠。
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節旨在介紹TypeScript中的函數,包括其基本結構、如何呼叫函數、函數的參數以及函數的返回值等相關概念。通過本章節,讀者可以學習到如何在TypeScript中使用不同的方式來定義函數,如函數聲明、函數表達式、箭頭函數和匿名函數等。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
TypeScript是一種由Microsoft開發和維護的開源編程語言。它是JavaScript的超集,主要擴展了JavaScript的語法,增加了靜態類型檢查和其他特性,使得開發大型應用程序更為方便和可靠。
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找