Skip to content
此页目录
本文总阅读量

Vue3 TS typescipt 类型标注

Prop 标注类型

defineProps运行时定义

宏函数会在运行时会推导出类型。

vue
<script setup lang="ts">
const props = defineProps({
  foo: { type: String, required: true }, // string
  bar: Number, // number | undefined
})
</script>

使用 ts 基于类型定义

  • 泛型定义
vue
<script setup lang="ts">
const props = defineProps<{
  foo: string
  bar?: number
}>()
</script>
  • 接口定义
vue
<!-- or -->
<script setup lang="ts">
interface Props {
  foo: string
  bar?: number
}
const props = defineProps<Props>()
</script>
  • 不支持外部文件
js
// 不支持,(目前vue版本:v3.2.37),这个限制可能会在未来的版本中被解除。
import { Props } from './file'

defineProps<Props>()
  • 泛型默认值 测试
vue
<script setup lang="ts">
interface Props {
  foo: string
  bar?: number
}
// 默认值会被编译为等价的运行时选项
const { foo, bar = 100 } = defineProps<Props>()
</script>

默认值功能目前测试阶段,需要显式启用:

js
// vite.config.js
export default {
  plugins: [
    vue({
      reactivityTransform: true,
    }),
  ],
}

emit 标注类型

vue
<script setup lang="ts">
// 运行时
const emit = defineEmits(["change", "update"])

// 基于类型
const emit = defineEmits<{
  (e: "change", id: number): void
  (e: "update", value: string): void
}>()
</script>

ref 标注类型

运行时

js
import { ref } from "vue"
const year = ref(2020) // 推导出的类型:Ref<number>
year.value = "2020" // => TS 错误

Ref 定义

js
import { ref } from "vue"
import type { Ref } from "vue"

const year: Ref<string | number> = ref("2020")

year.value = 2020 // 成功!

泛型定义

js
const year = ref<string | number>('2020') // Ref<string | number>

year.value = 2020 // 成功!

const n = ref<number>() // 没有给初始值,推导得到的类型:Ref<number | undefined>

reactive 标注类型

运行时

js
import { reactive } from "vue"
const book = reactive({ title: "Vue" }) // 推导得到的类型:{ title: string }

接口定义

js
import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue' })

TIP

不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。

computed 标注类型

运行时推导

js
import { ref, computed } from "vue"
const count = ref(0)
const double = computed(() => count.value * 2) // 推导得到的类型:ComputedRef<number>
const result = double.value.split("") // TS 报错,number 不存在此方法

泛型定义

js
const double =
  computed <
  number >
  (() => {
    // 若返回值不是 number 类型则会报错
  })

事件处理器标注类型

vue
<script setup lang="ts">
function handleChange(event) {
  // `event` 隐式地标注为 `any` 类型
  console.log(event.target.value)
}

// or
function handleInputChange(event: Event) {
  // 如果标注了类型,可能需要断言 event 上的属性某些类型
  console.log((event.target as HTMLInputElement).value)
}
</script>

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

provide/inject 标注类型

InjectionKey 注入 Symbol

祖孙传值一般会在不同的组件中运行,需要用到Symbol来处理类型定义。vue 提供

js
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'

const key = Symbol() as InjectionKey<string>

provide(key, 'foo') // 若提供的是非字符串值会导致错误

const foo = inject(key) // foo 的类型:string | undefined

字符串注入

js
const foo = inject<string>('foo') // 类型:string | undefined

const foo = inject<string>('foo','bar') // 类型:string 提供了默认值 'bar' 则类型为 string
// or
const foo = inject<string>('foo') as string // 类型:string 断言它始终存在

模板 ref 标注类型

vue
<script setup lang="ts">
import { ref, onMounted } from "vue"

const el = ref<HTMLInputElement | null>(null)

onMounted(() => {
  el.value?.focus() // 严格的类型安全,需要用可选链,初始值和 v-if 都会是 null
})
</script>

<template>
  <input ref="el" />
</template>

组件模板 ref 标注类型

有些时候经常需要使用 ref 获取子组件的实例:

vue
<!-- Son.vue -->
<script setup lang="ts">
import { ref } from "vue"

const isContentShown = ref(false)
const open = () => (isContentShown.value = true)

defineExpose({
  open,
})
</script>

先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:

vue
<!-- App.vue -->
<script setup lang="ts">
import MyModal from "./MyModal.vue"

const modal = ref<InstanceType<typeof MyModal> | null>(null)

const openModal = () => {
  modal.value?.open()
}
</script>

评论

交流群