Let's make a list component that works with an array of objects.
<script setup lang="ts">
const props = defineProps<{
items: Record<string, unknown>[],
selected: Record<string, unknown> | null
field: string
}>()
const emit = defineEmits<{
(e: 'update:selected', value: Record<string, unknown> | null): void
}>()
</script>
<template>
<div v-for="(item,idx) in items" :key="idx">
<div
@click="emit('update:selected',item)"
style="cursor: pointer">
{{ item[field] }}
</div>
</div>
</template>
Let's try to use it by passing a list of employees.
<script setup lang='ts'>
import MyList from './MyList.vue'
import {Ref, ref} from "vue";
interface Employee {
name: string;
age: number;
}
const employees: Employee[] = [
{name: 'Mike', age: 34},
{name: 'Kate', age: 19},
{name: 'Den', age: 54},
]
const selectedEmployee=ref<Employee | null>(null)
</script>
<template>
Age: {{selectedEmployee?selectedEmployee.age:'not selected'}}
<MyList :items="employees" v-model:selected="selectedEmployee" field="name"/>
</template>
Everything is working. But, if you do a build, an error occurs TS2322: “Type 'Employee' is not assignable to type 'Record<string, unknown>'". A generic component would be the solution. But it's not there yet. What is the best way to solve this problem? vue playground
CodePudding user response:
Try to extract the Employee
interface and put inside another file, then import it in both files and use it instead of Record<string, unknown>
:
employee.ts
export default interface Employee {
name: string;
age: number;
}
MyList.vue
<script setup lang="ts">
import Employee from './employee'
const props = defineProps<{
items: Employee[],
selected: Employee | null
field: string
}>()
const emit = defineEmits<{
(e: 'update:selected', value: Employee | null): void
}>()
</script>
App.vue
<script setup lang='ts'>
import MyList from './MyList.vue'
import {Ref, ref} from "vue";
import Employee from './employee'
const employees: Employee[] = [
{name: 'Mike', age: 34},
{name: 'Kate', age: 19},
{name: 'Den', age: 54},
]
const selectedEmployee=ref<Employee | null>(null)
</script>
CodePudding user response:
It is possible to create a method.
export const wrapSelected = <T>(value: Ref<T | null>) => {
return computed<Record<string, unknown> | null>({
get: () => value.value as unknown as (Record<string, unknown> | null),
set: (val: Record<string, unknown> | null) => {
value.value = val as unknown as T | null
}
})
}
App.vue
...
const wrapperSelected=wrapSelected(selectedEmployee)
</script>
<template>
Age: {{ selectedEmployee ? selectedEmployee?.age : 'not selected' }}
<my-list :items="wrapperItems" v-model:selected="wrapperSelected" field-name="name"/>
</template>
Though it's not very aesthetically pleasing. It's better than good old any?