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 人的一天

最详细的 Vue3 + TypeScript 使用教程【值得收藏】 - 掘金

# Vue # TypeScript