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 Ref
const 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 = 777
onMounted(() => {
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.ts
export 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
- 原本的寫法
// Normal
const props = defineProps({
initialCount: {
type: Number,
default: 0
}
})
const { count } = useAutoCount(props.initialCount)
function reset() {
count.value = 0
}
- 使用泛型簡單定義類型
<script setup lang='ts'>
// 寫法1 等同 msg 的required = true
defineProps<{ msg: string }>()
// 寫法2 等同 msg 的required = false
const 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'
// 寫法1
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 寫法2
const emit = defineEmits<{
change: [id: number]
update: [value: string]
}>()
// id必須是數字
function triggerChange() {
const id = 1
emit('change', id)
}
// value必須是string
function triggerUpdate() {
const value = 'Hello, world!'
emit('update', value)
}
</script>
- 寫法的差異
// runtime
const emit = defineEmits(['change', 'update'])
// type-based
const 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