TypeScript in Vue3
使用 script 語法糖
<script setup lang="ts"> ......</script>
使用 ref
- 使用泛型指定類別
import { ref } from 'vue'import type { Ref } from 'vue'// 1.自動偵測型別const num = ref(0)num.value = 'string' //error// 2.使用泛型const num = ref<string | number>('2023')const bool = ref<boolean>(true)// 3.使用type Refconst num: Ref<string | number> = ref('2023')
- 定義物件的類型
interface User { name: string age: string | number}const user = ref<User>({ name: 'Charles', age: 24})
- 如果不指定型別
// 原本沒有指定類別,在元件掛載時,無法賦予除null以外的值const count = ref(null)// 使用泛型,修改成可以賦予number or null 型別的值const count = ref<number | null>(null)count.value = 777onMounted(() => { fetchCount((initialCount) => { count.value = initialCount })})
使用 reactive
- 可以使用 union 定義類型
const pet = reactive({ name: "Dogee" age: 3 as string | number})pet.age = "7" // ok
- 使用 interface (推薦)
import { reactive } from 'vue'interface User { name: string age: string | number}const user: User = reactive({ name: 'Ham', age: '20'})// year屬性可選interface Book { title: string year?: number}const book: Book = reactive({ title: 'Vue 3 Guide' })
with Interface
- 在 src 內建立 types 資料夾
- 先在 types 資料夾建立 job.ts 檔案
- 建立 Job 的 interface 輸出
更新提示:2023/12/28 更新寫法
// src/types/job.tsexport interface Job { title: string location: string salary: number id: string}
- 在要使用的元件裡引入
- 引入 TypeScript 類型,需要加上前綴
type
<script setup lang='ts'>import { type Job } from '@/types/job'// jobs會是一個陣列,裡面的物件要符合Job interface的限制const jobs = ref<Job[]>([{ title: 'frontend engineer', location: 'Kaohsiung', salary: 60000, id: '999'}])</script>
with Props
- 原本的寫法
// Normalconst props = defineProps({ initialCount: { type: Number, default: 0 }})const { count } = useAutoCount(props.initialCount)function reset() { count.value = 0}
- 使用泛型簡單定義類型
<script setup lang='ts'>// 寫法1 等同 msg 的required = truedefineProps<{ msg: string }>()// 寫法2 等同 msg 的required = falseconst props = defineProps<{ msg?: string }>()</script>
- 當 props 是一個物件時
<script setup lang="ts">interface Book { title: string author: string year: number}const props = defineProps<{ book: Book}>()</script>
- 使用 withDefaults 設定預設值
// 語法withDefaults(props, value)
// 範例const props = withDefaults(defineProps<{ initialCount: number }>(), { initialCount: 0})
- with interface
interface Props { initialCount?: number}const props = withDefaults(defineProps<Props>(), { initialCount: 0})
with Emit
- 原本的寫法
<template> <div> <button @click="triggerChange">觸發 change 事件</button> <button @click="triggerUpdate">觸發 update 事件</button> </div></template><script setup>import { defineEmits } from 'vue'const emit = defineEmits(['change', 'update'])function triggerChange() { const id = 1 emit('change', id)}function triggerUpdate() { const value = 'Hello, world!' emit('update', value)}</script>
- 加入TypeScript,指定參數的類型
<template> <div> <button @click="triggerChange">觸發 change 事件</button> <button @click="triggerUpdate">觸發 update 事件</button> </div></template><script setup lang="ts">import { defineEmits } from 'vue'// 寫法1const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void}>()// 寫法2const emit = defineEmits<{ change: [id: number] update: [value: string]}>()// id必須是數字function triggerChange() { const id = 1 emit('change', id)}// value必須是stringfunction triggerUpdate() { const value = 'Hello, world!' emit('update', value)}</script>
- 寫法的差異
// runtimeconst emit = defineEmits(['change', 'update'])// type-basedconst emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void}>()
Computed
- 不指定類型,自動檢測
import { ref, computed } from 'vue'const count = ref(0)// inferred type: ComputedRef<number>const double = computed(() => count.value * 2)// => TS Error: Property 'split' does not exist on type 'number'const result = double.value.split('')
- 指定回傳的類型
const double = computed<number>(() => { // type error if this doesn't return a number})
Event Handler
- 抓取 DOM 節點
function handleChange(event: Event) { console.log((event.target as HTMLInputElement).value)}
Index Signatures 索引簽名
- 當物件內有很多屬性時可以使用
- 有時候事先並不知道屬性名稱,或是未來會新增屬性
- 表示物件的資料結構
- 例如下面的 interface 所定義的物件 ⇒ key 會是 string , value 會是 number
interface ConversionRates { [currency: string]: number}const conversionRates: ConversionRates = { USD: 1.23, EUR: 0.89, GBP: 0.76}
參考資料
Vue 3 TypeScript Crash Course 2023
Two Things You Need for Script Setup in Vue
TypeScript with Composition API | Vue.js
Day16:【TypeScript 學起來】新增任意屬性的好方法:Index Signatures 索引簽名 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天
# Vue # TypeScript