Background
I'm trying to create reusable data table component with dynamic column formatting and typing in Vue 3. This table accepts data rows and column definitions, including components to display this column as well as value getters.
Data Table
<script setup lang="ts">
import type { Component } from 'vue';
export interface IColumn<T = any> {
id: number;
/**
* Vue component that will be used to display the column.
* This component always has a `data` property.
*/
format?: Component;
/**
* Function returning the value that will be passed
* to the `format` component via `data` property.
*/
value?: (row: T) => unknown; // ‼️ I need a corresponding return type here
}
defineProps<{
cols: IColumn[];
rows: any[];
}>();
</script>
<template>
<table>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td v-for="col in cols" :key="col.id">
<component :is="col.format" :data="col.value(row)" />
</td>
</tr>
</tbody>
</table>
</template>
Question
❓ I'm struggling with a proper return type for the value function.
Currently I'm using unknown, but it should be based on the data prop accepted by the format component. Seems that this should involve some deep TypeScript magic, but I'm unable to get anywhere.
Usage Example
Here is the usage example to make it a little bit more clear:
<script setup lang="ts">
// Omitted imports go here...
interface User {
id: number;
name: string;
created_at: string;
expire_at: string;
}
const rows: User[] = [
{ id: 1, name: 'John', created_at: '2022-10-10', expire_at: '2022-10-11' },
{ id: 2, name: 'Jane', created_at: '2022-07-10', expire_at: '2022-11-11' },
{ id: 3, name: 'Anna', created_at: '2022-10-07', expire_at: '2023-11-10' },
];
const cols: IColumn<User>[] = [
{
label: 'ID',
format: FNumber,
value: (user) => user.id,
},
{
label: 'Name',
format: FString,
value: (user) => user.name,
},
{
label: 'Activity',
format: FDateRange,
value: (user) => ({
dateFrom: user.created_at,
dateTo: user.expire_at,
}),
},
];
Format Component Example
Here is the example of FDateRange.vue that can be used as IColumn's format
<script setup lang="ts">
defineProps<{
data: {
dateFrom: Date;
dateTo: Date;
};
}>();
</script>
<template>
<span>{{ (new Date(data.dateFrom)).toLocaleDateString() }}</span>
–
<span>{{ (new Date(data.dateTo)).toLocaleDateString() }}</span>
</template>