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>
最优解决方案
核心思路
- 通过类型工具提取
Item中属于Common[]类型的键,避免操作非数组或不含id的属性(比如oneKey); - 利用泛型关联键与对应数组元素的类型,让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>{{ 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
相关产品推荐
相关产品推荐

