Ali Vad
Ali Vad

Reputation: 29

Make common function with typescript

I have a vue3 component. It shows list of items. And there is a function to delete item. The problem is that i need check param, and write in which array apply filter. I want to make common function to do this with 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>



this is my solution, with using keyof Item

<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);
  }
} // and here i got an error: No overload matches this call.
</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>

think must be better solution with more features of typescript

Upvotes: 2

Views: 311

Answers (1)

g.p
g.p

Reputation: 370

The only problem here is that oneKey is defined as a string. Rest all attributes are of type array. Thats the reason you cannot do (items.value[entity] as T[]). Coz not everything is an array. If you keep oneKey in a separate type, this can be accomplished by

const deleteItems = (item: Item, name: keyof Item, id: Common['id']): Item => ({
  ...items,
  [name]: items[name].filter((d: Common) => d.id !== id),
});

Always prefer immutability. This keeps the function deleteItems pure and you can re-assign the returned values

Upvotes: 1

Related Questions