IDE 支持
- Volar 是官方的 VSCode 扩展,提供了 Vue 单文件组件中的 TypeScript 支持,还伴随着一些其他非常棒的特性。
- TypeScript Vue Plugin 用于支持在 TS 中 import *.vue 文件。
配置 tsconfig.json
通过 create-vue 搭建的项目包含了预先配置好的 tsconfig.json。
手动配置 tsconfig.json 时,请留意以下选项:
- compilerOptions.isolatedModules 应当设置为 true,因为 Vite 使用 esbuild 来转译 TypeScript,并受限于单文件转译的限制。
- 如果你在构建工具中配置了路径解析别名,例如 @/* 这个别名被默认配置在了 create-vue 项目中,你需要通过 compilerOptions.paths 选项为 TypeScript 再配置一遍。
Volar Takeover 模式
在每个项目里我们都运行了两个语言服务实例:一个来自 Volar,一个来自 VSCode 的内置服务。这在大型项目里可能会带来一些性能问题。为了优化性能,Volar 提供了一个叫做“Takeover 模式”的功能。在这个模式下,Volar 能够使用一个 TS 语言服务实例同时为 Vue 和 TS 文件提供支持
- 在当前项目的工作空间下,用 Ctrl + Shift + P (macOS:Cmd + Shift + P) 唤起命令面板。
- 输入 built,然后选择“Extensions:Show Built-in Extensions”。
- 在插件搜索框内输入 typescript (不要删除 @builtin 前缀)。
- 点击“TypeScript and JavaScript Language Features”右下角的小齿轮,然后选择“Disable (Workspace)”。
- 重新加载工作空间。Takeover 模式将会在你打开一个 Vue 或者 TS 文件时自动启用。
defineComponent()
为了让 TypeScript 正确地推导出组件选项内的类型,我们需要通过 defineComponent() 这个全局 API 来定义组件
| import { defineComponent } from 'vue' |
| |
| export default defineComponent({ |
| |
| props: { |
| message: String |
| }, |
| setup(props) { |
| props.message |
| } |
| }) |
复制
为组件的 props 标注类型
为了生成正确的运行时代码,传给 defineProps() 的泛型参数必须是以下之一:
- defineProps<{ /*… */ }>()
- interface Props {/* … */} defineProps()
| <script setup lang="ts"> |
| const props = defineProps<{ |
| foo: string |
| bar?: number |
| }>() |
| </script> |
| |
| <script setup lang="ts"> |
| interface Props { |
| foo: string |
| bar?: number |
| } |
| |
| const props = defineProps<Props>() |
| </script> |
| |
| |
复制
withDefaults 编译器
为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决:
| export interface Props { |
| msg?: string |
| labels?: string[] |
| } |
| |
| const props = withDefaults(defineProps<Props>(), { |
| msg: 'hello', |
| labels: () => ['one', 'two'] |
| }) |
复制
复杂的 prop 类型
使用 PropType 工具类型:
| import type { PropType } from 'vue' |
| |
| const props = defineProps({ |
| book: Object as PropType<Book> |
| }) |
| |
| |
| import { defineComponent } from 'vue' |
| import type { PropType } from 'vue' |
| |
| interface Book { |
| title: string |
| author: string |
| year: number |
| } |
| |
| export default defineComponent({ |
| props: { |
| book: Object as PropType<Book> |
| } |
| }) |
复制
组件的 emits 标注类型
| <script setup lang="ts"> |
| |
| const emit = defineEmits(['change', 'update']) |
| |
| |
| const emit = defineEmits<{ |
| (e: 'change', id: number): void |
| (e: 'update', value: string): void |
| }>() |
| </script> |
复制
ref() 标注类型
| import { ref } from 'vue' |
| import type { Ref } from 'vue' |
| |
| const year: Ref<string | number> = ref('2020') |
| |
| year.value = 2020 |
复制
或者,在调用 ref() 时传入一个泛型参数
| |
| const year = ref<string | number>('2020') |
| |
| year.value = 2020 |
复制
reactive() 标注类型
要显式地标注一个 reactive 变量的类型,我们可以使用接口:
| import { reactive } from 'vue' |
| |
| interface Book { |
| title: string |
| year?: number |
| } |
| |
| const book: Book = reactive({ title: 'Vue 3 指引' }) |
复制
computed() 标注类型
通过泛型参数显式指定类型:
| const double = computed<number>(() => { |
| |
| }) |
复制
事件处理函数标注类型
| <script setup lang="ts"> |
| function handleChange(event) { |
| |
| console.log(event.target.value) |
| } |
| </script> |
| |
| <template> |
| <input type="text" @change="handleChange" /> |
| </template> |
| |
| |
| function handleChange(event: Event) { |
| console.log((event.target as HTMLInputElement).value) |
| } |
复制
为 provide / inject 标注类型
Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:
| import { provide, inject } from 'vue' |
| import type { InjectionKey } from 'vue' |
| |
| const key = Symbol() as InjectionKey<string> |
| |
| provide(key, 'foo') |
| |
| const foo = inject(key) |
复制
| const foo = inject<string>('foo') |
| 当提供了一个默认值后,这个 undefined 类型就可以被移除: |
| const foo = inject<string>('foo', 'bar') |
| 如果你确定该值将始终被提供,则还可以强制转换该值: |
| const foo = inject('foo') as string |
复制
模板引用标注类型
| <script setup lang="ts"> |
| import { ref, onMounted } from 'vue' |
| |
| const el = ref<HTMLInputElement | null>(null) |
| |
| onMounted(() => { |
| el.value?.focus() |
| }) |
| </script> |
| |
| <template> |
| <input ref="el" /> |
| </template> |
复制
组件模板引用标注类型
为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:
| <!-- 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> |
复制
如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el。
| import { ref } from 'vue' |
| import type { ComponentPublicInstance } from 'vue' |
| |
| const child = ref<ComponentPublicInstance | null>(null) |
复制