You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3组件中使用TypeScript编写通用删除项函数的优化方案

优化Vue3 + TypeScript通用删除函数方案

我有一个Vue3组件,用于展示cats、cars等多组列表项,当前的删除函数需判断参数来确定对哪个数组执行过滤删除操作。希望用TypeScript编写通用删除函数优化逻辑,尝试用keyof Item实现时出现“No overload matches this call”错误,寻求符合TypeScript特性的最优方案。

原始代码

<template>
  <div v-for="cat in items.cats" :key="cat.id" :style="{ display: ' flex' }">
    <div>{{ cat }}</div>
    <button type="button" @click="deleteItem('cats', cat.id)">delete</button>
  </div>

  <div v-for="car in items.cars" :key="car.id" :style="{ display: ' flex' }">
    <div @delete="deleteItem('cars', car.id)">{{ car }}</div>
    <button type="button" @click="deleteItem('cars', car.id)">delete</button>
  </div>
</template>

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

interface Common {
  id: number;
}

interface Cat extends Common {
  name: string;
  type: string;
}

interface Car extends Common {
  model: string;
  serial: string;
}

interface Item {
  cats: Cat[];
  cars: Car[];
  anotherArray: [];
  oneMoreArray: [];
  oneKey: string;
}

const items = ref<Item>({
  cats: [
    {
      id: 0,
      name: 'Kitty',
      type: 'home',
    },
    {
      id: 1,
      name: 'Sherhan',
      type: 'jungle',
    },
  ],

  cars: [
    {
      id: 0,
      model: 'BMW',
      serial: 'X5',
    },
    {
      id: 1,
      model: 'Audi',
      serial: 'Q5',
    },
  ],

  anotherArray: [],
  oneMoreArray: [],
  oneKey: '',
});

function deleteItem(name?: 'cats' | 'cars', id?: number) {
  if (id !== undefined && name === 'cats') {
    items.value.cats = items.value.cats?.filter((cat: Cat) => cat.id !== id);
  }

  if (id !== undefined && name === 'cars') {
    items.value.cars = items.value.cars?.filter((car: Car) => car.id !== id);
  }
}
</script>

尝试的方案(报错)

尝试用keyof Item实现通用函数,但出现“No overload matches this call”错误:

<script setup lang="ts">
function myFunc<T>(entity: keyof Item, id?: number) {
  if (id) {
    (items.value[entity] as T[]) = (items.value[entity] as T[]).filter((item: Common) => item.id !== id);
  }
}
</script>

<template>
  <div v-for="cat in items.cats" :key="cat.id" :style="{ display: ' flex' }">
    <div>{{ cat }}</div>
    <button type="button" @click="deleteItem < Cat > ('cats', cat.id)">delete</button>
  </div>

  <div v-for="car in items.cars" :key="car.id" :style="{ display: ' flex' }">
    <div>{{ car }}</div>
    <button type="button" @click="deleteItem < Car > ('cars', car.id)">delete</button>
  </div>
</template>

最优解决方案

核心思路

  1. 通过类型工具提取Item中属于Common[]类型的键,避免操作非数组或不含id的属性(比如oneKey);
  2. 利用泛型关联键与对应数组元素的类型,让TypeScript自动推断,无需手动指定泛型;
  3. 确保函数操作的安全性,避免类型断言带来的潜在问题。

优化后完整代码

<template>
  <div v-for="cat in items.cats" :key="cat.id" :style="{ display: 'flex' }">
    <div>{{ cat }}</div>
    <button type="button" @click="deleteItem('cats', cat.id)">delete</button>
  </div>

  <div v-for="car in items.cars" :key="car.id" :style="{ display: 'flex' }">
    <div>{{ car }}</div>
    <button type="button" @click="deleteItem('cars', car.id)">delete</button>
  </div>
</template>

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

interface Common {
  id: number;
}

interface Cat extends Common {
  name: string;
  type: string;
}

interface Car extends Common {
  model: string;
  serial: string;
}

interface Item {
  cats: Cat[];
  cars: Car[];
  anotherArray: [];
  oneMoreArray: [];
  oneKey: string;
}

// 提取Item中值为Common[]类型的键
type EntityKey = {
  [K in keyof Item]: Item[K] extends Common[] ? K : never;
}[keyof Item];

const items = ref<Item>({
  cats: [
    { id: 0, name: 'Kitty', type: 'home' },
    { id: 1, name: 'Sherhan', type: 'jungle' },
  ],
  cars: [
    { id: 0, model: 'BMW', serial: 'X5' },
    { id: 1, model: 'Audi', serial: 'Q5' },
  ],
  anotherArray: [],
  oneMoreArray: [],
  oneKey: '',
});

// 通用删除函数,自动推断泛型
function deleteItem<K extends EntityKey>(entity: K, id: number) {
  const targetArray = items.value[entity] as (Item[K] extends (infer T)[] ? T : never)[];
  items.value[entity] = targetArray.filter(item => item.id !== id) as Item[K];
}
</script>

方案说明

  • 类型安全:EntityKey只允许传入cats、cars这类符合条件的键,避免误操作oneKey等非目标属性;
  • 自动推断:函数调用时TypeScript会根据传入的entity自动推断对应的数组元素类型,无需手动指定泛型(Vue模板也不支持手动传泛型,这也是之前报错的关键原因);
  • 扩展性强:后续新增其他符合Common接口的数组(比如dogs: Dog[]),只需在Item中添加定义,无需修改删除函数。

内容的提问来源于stack exchange,提问作者Ali Vad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 01:09:09