0
点赞
收藏
分享

微信扫一扫

如何在Vue 3 + TypeScript中使用类型守卫?


在Vue 3 + TypeScript中使用类型守卫(Type Guard)可以帮助你在组件中安全地处理复杂类型,避免类型断言(as)带来的潜在风险。类型守卫的核心是通过一个函数判断变量类型,让TypeScript在编译时就能识别变量的具体类型。

1. 基本使用场景:验证接口数据

最常见的场景是处理后端返回的不确定类型数据,确保它符合组件所需的接口类型。

步骤1:定义接口和类型守卫函数

<!-- 子组件 Child.vue -->
<template>
  <div v-if="isValidUser">
    <p>姓名: {{ user.name }}</p>
    <p>年龄: {{ user.age }}</p>
  </div>
  <div v-else>数据格式错误</div>
</template>

<script setup lang="ts">
import { defineProps, computed } from 'vue';

// 1. 定义需要验证的接口
interface User {
  name: string;
  age: number;
  email?: string; // 可选属性
}

// 2. 定义类型守卫函数(核心)
// 函数返回值为 `data is User`,表示"如果返回true,则data是User类型"
function isUser(data: unknown): data is User {
  // 逐步验证:先判断是对象且不为null
  if (data === null || typeof data !== 'object') {
    return false;
  }
  
  // 验证必填属性的类型
  const user = data as User;
  return (
    typeof user.name === 'string' &&  // name必须是字符串
    typeof user.age === 'number' &&   // age必须是数字
    (user.email === undefined || typeof user.email === 'string')  // 可选属性验证
  );
}

// 3. 接收父组件传递的可能不安全的数据
const props = defineProps<{
  rawData: unknown; // 明确标记为未知类型,需要验证
}>();

// 4. 使用类型守卫验证数据
const isValidUser = computed(() => isUser(props.rawData));
const user = computed(() => isValidUser.value ? props.rawData : null);
</script>

步骤2:父组件传递数据

<!-- 父组件 Parent.vue -->
<template>
  <Child :raw-data="backendData" />
</template>

<script setup lang="ts">
import Child from './Child.vue';

// 模拟后端返回的原始数据(可能格式错误)
const backendData = {
  name: "张三",
  age: "25" // 这里故意传字符串,模拟错误
};
</script>

此时子组件会通过isUser函数检测到age是字符串而非数字,显示"数据格式错误",避免了直接断言导致的运行时错误。

2. 在事件处理中使用类型守卫

处理组件事件时,也可以用类型守卫区分不同的事件参数类型:

<template>
  <button @click="handleEvent">触发事件</button>
</template>

<script setup lang="ts">
import { defineEmits } from 'vue';

// 定义两种可能的事件参数类型
type EventData = 
  | { type: 'success'; message: string }
  | { type: 'error'; code: number };

// 定义事件
const emit = defineEmits<{
  (e: 'action', data: EventData): void;
}>();

// 触发事件
const handleEvent = () => {
  emit('action', { type: 'success', message: '操作成功' });
};

// 类型守卫:判断是否为success类型
function isSuccessEvent(data: EventData): data is { type: 'success'; message: string } {
  return data.type === 'success';
}

// 使用类型守卫处理事件
const handleAction = (data: EventData) => {
  if (isSuccessEvent(data)) {
    // TypeScript自动推断data为success类型,可直接访问message
    console.log('成功消息:', data.message);
  } else {
    // 否则自动推断为error类型,可访问code
    console.log('错误代码:', data.code);
  }
};
</script>

3. 与ref/reactive结合使用

在响应式数据中使用类型守卫,可以安全地访问复杂类型的属性:

<script setup lang="ts">
import { ref } from 'vue';

interface Product {
  id: number;
  name: string;
}

// 类型守卫
function isProduct(data: unknown): data is Product {
  return (
    typeof data === 'object' && 
    data !== null && 
    typeof (data as Product).id === 'number' &&
    typeof (data as Product).name === 'string'
  );
}

// 响应式数据(初始为unknown)
const product = ref<unknown>(null);

// 模拟从API获取数据
const fetchData = async () => {
  const res = await fetch('/api/product');
  const data = await res.json();
  
  // 验证通过后再赋值
  if (isProduct(data)) {
    product.value = data; // 此时TypeScript会识别product为Product类型
  } else {
    console.error('产品数据格式错误');
  }
};

// 使用时的安全访问
const logProduct = () => {
  if (isProduct(product.value)) {
    console.log('产品名称:', product.value.name); // 安全访问
  }
};
</script>

核心要点总结

  1. 类型守卫函数:返回值格式为参数 is 类型,告诉TypeScript如何判断类型。
  2. 渐进式验证:对复杂类型(如对象),要逐一验证必填属性的类型,避免遗漏。
  3. 与Vue特性结合:在props接收、响应式数据处理、事件传递等场景中,先用类型守卫验证,再安全使用。
  4. 替代类型断言:相比as,类型守卫通过实际检查确保类型正确,减少运行时错误。

简单说,类型守卫就像给数据"验明正身",只有通过检查的才能被认定为目标类型,让TypeScript和你的代码更"放心"。


举报

相关推荐

0 条评论