Skip to content
On this page

TypeScript 应用

创建 vue-ts 项目

创建一个基于 ts 的 vue 项目,来学习 ts 语法

bash
# npm 6.x
npm create vite@latest my-vue-ts-app --template vue-ts

# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-ts-app -- --template vue-ts

# yarn
yarn create vite my-vue-ts-app --template vue-ts

# pnpm
pnpm create vite my-vue-ts-app --template vue-ts

在基于 vite 的项目中可以直接验证 ts 代码结果,因为已经配置好了 ts 环境。

① TypeScript与Vue

TIP

typescript 配合 Vue3 composition-api 使用

https://staging-cn.vuejs.org/guide/typescript/composition-api.html

前提:script 加上 lang="ts" 才能写ts代码

vue
<script setup lang="ts"></script>

defineProps的TS写法

  1. defineProps 的基本使用:
ts
const props = defineProps({
  money: {
    type: Number,
    required: true
  },
  car: {
    type: String,
    required: false,
    default: '宝马车'
  }
})
console.log(props.money) // number
console.log(props.car) // string | undefined
  1. defineProps 通过泛型参数来定义 props 的类型通常更直接:
ts
// 👎
const props = defineProps<{
  money: number
  car?: string
}>()

// 👍 推荐
interface Props {
  money: number
  car?: string
}

const props = defineProps<Props>()
  1. 如果需要给 props 设置默认值,👎需要使用 withDefaults 函数:
ts
const props = withDefaults(defineProps<Props>(),{
  car: '宝马车'
})
  1. 上面写法太笨拙,可以使用 响应式语法糖 解构 + defineProps 就行:
ts
const { money, car = "宝马车" } = defineProps<Props>();

注意:目前需要 显式地选择开启 ,因为它还是一个实验性特性。

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

扩展-了解TS中函数的调用签名

ts
// 普通函数的写法
type MyFn = (name: "changeNum" | "changeStr", value: number | string ) => void

const fn: MyFn = (name, value) => {};

fn("changeNum", "1");
fn("changeNum", 1);
fn("changeStr", "1");

扩展:TS语法 调用签名

JS中万物皆对象, 函数也属于对象类型

ts
interface MyFn {
  (name: "changeNum", value: number): void;
  (name: "changeStr", value: string): void;
}

const fn: MyFn = (name, value) => {};

fn("changeNum", "1"); // ❌会报错
fn("changeMsg", 1); // ❌会报错

fn("changeNum", "1"); // ✅
fn("changeMsg", 1); // ✅

defineEmits的TS写法

  1. defineEmits 的JS用法:
ts
const emit = defineEmits(['changeMoney', 'changeCar'])
  1. defineEmits 通过泛型参数来定义,可以实现更细粒度的校验:
ts
// 👎·
const emit = defineEmits<{
  (e: 'changeMoney', money: number): void
  (e: 'changeCar', car: string): void
}>()

// 👍·
interface Emits  {  
  (e: 'changeMoney', money: number): void
  (e: 'changeCar', car: string): void
}
const emit = defineEmits<Emits>()

ref的TS写法

ref() 会隐式的依据数据推导类型

  1. 如果是简单类型,推荐使用类型推导:
ts
// const money = ref<number>(10)

const money = ref(10)
  1. 如果是复杂类型,推荐指定泛型:
ts
type Todo = {
  id: number
  name: string
  done: boolean
}
const list = ref<Todo[]>([])

setTimeout(() => {
  list.value = [
    { id: 1, name: '吃饭', done: false },
    { id: 2, name: '睡觉', done: true }
  ]
}, 1000)

复杂数据一般是后台返回数据,默认值是空,无法进行类型推导。

小结:

  1. 简单数据类型,省略泛型
  2. 复杂数据类型,完整写法

reactive的TS写法

reactive() 也会隐式的依据数据推导类型

  1. 默认值属性是固定的,推荐使用类型推导:
ts
// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue3 在线医疗' })
  1. 根据默认值推导不出我们需要的类型,推荐使用接口或者类型别名给变量指定类型:
ts
// 我们想要的类型:{ title: string, year?: number }
type Book = {
  title: string
  year?: number
}
const book: Book = reactive({ title: 'Vue3 在线医疗' })
book.year = 2022
  • 官方:不推荐使用 reactive() 的泛型参数,因为底层和 ref() 实现不一样。

computed和TS

  1. computed() 会从其计算函数的返回值上推导出类型:
ts
import { ref, computed } from 'vue'

const count = ref(100);
const doubleCount = computed(() => count.value * 2);
  1. 可以通过泛型参数显式指定类型:
ts
const doubleMoney = computed<string>(() => (count.value * 2).toFixed(2));

事件处理与TS

  1. 不加类型,event默认是any,类型不安全:
vue
<script setup lang="ts">
// 提示:参数“event”隐式具有“any”类型。  
const handleChange = (event) => {
  console.log(event.target.value)
}
</script>

<template>
  <input type="text" @change="handleChange" />
</template>
  1. 处理类型:
ts
// `event` 隐式地标注为 `any` 类型,如何指定:event 类型?
// 1. @change="handleChange($event)"" 查看$event类型
// 2. 鼠标悬停事件 @change 查看类型
const handleChange = (event: Event) => {
  // `event.target` 是 `EventTarget | null` 类型,如何指定具体类型?
  // document.querySelector('input') 查看返回值类型
  console.log((event.target as HTMLInputElement).value)
}

Template Ref与TS

模板 ref 需要通过一个显式指定的泛型参数,建议默认值 null

vue
<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>
  • 注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。
  • 这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null

非空断言

处理类型可能是 null 或 undefined 的值,下面的属性或函数的访问赋值:

  1. 可选链
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue';


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

onMounted(()=>{
  // 可选链:只能访问,不能赋值
  // input.value?.value = "123" ❌
  console.log(input.value?.value);
})

</script>

<template>
  <div>App组件</div>
  <input type="text" ref="input" value="abc">
</template>
  1. 逻辑判断
ts
// 逻辑判断, 类型守卫
  if (input.value) {
    console.log(input.value.value)
    input.value.value = '123'
  }
  1. 非空断言
ts
// 一定要确定不为空!!!
  console.log(input.value!.value)
  input.value!.value = '123'

Released under the MIT License.