Thoughts on your code
https://i.sstatic.net/QA62T.png
The reason for this issue is that the type of itemKey
is generic in the component definition and is only known at runtime. As a result, TypeScript's static type checking is unable to pinpoint the exact key it is referring to.
To resolve this issue, a more specific type definition for items[]
is required.
https://i.sstatic.net/76WEP.png
In Vue, the :key
attribute is designed to work with string
, number
, or symbol
values. Therefore, if the type of item[itemKey]
matches one of these types, it can be used as the key; otherwise, it cannot.
Possible Solution # 1 - T extends U ? U : never
- TypeScript 2.8 and higher
In this scenario, we assume that every value in the object T
can serve as a valid key. By utilizing item[itemKey]
, we are essentially stating this assertion, which leads to the error.
Array<{ [key in keyof T]: T[key] extends string | number | symbol ? T[key] : never }>
The items
prop is an array that, based on the keys (keyof T
) of type T
, selects the types from T[key]
that are either string
, number
, or symbol
. If the types do not match, the never
type is returned.
This ensures that only string
, number
, or symbol
values can be assigned to the :key
attribute.
<script setup lang="ts" generic="T, K extends keyof T">
defineProps<{
items: Array<{ [key in keyof T]: T[key] extends string | number | symbol ? T[key] : never }>
itemKey: K
}>()
</script>
<template>
<ul>
<li v-for="item in items" :key="item[itemKey]">
<slot :item="item" />
</li>
</ul>
</template>
Possible Solution # 2 (declare item[itemKey]
type)
If the object can contain values of various types, performing precise type checking may not be worth the effort. It is recommended to declare `item[itemKey]` as a value that aligns with our expectations for future use. You may also consider implementing runtime JavaScript checks to ensure the type of `item[itemKey]` aligns with your expectations and display error messages in the console during development mode.
<script setup lang="ts" generic="T, K extends keyof T">
defineProps<{
items: Array<T>
itemKey: K
}>()
</script>
<template>
<ul>
<li v-for="item in items" :key="(item[itemKey as K] as string | number | symbol)">
<slot :item="item" />
</li>
</ul>
Possible Solution # 3 (convert to string)
If ensuring a string value for the :key
attribute is crucial, simply convert the value of item[itemKey]
to a string. By explicitly converting it to a string, TypeScript won't raise any issues as it meets the requirements of the :key
attribute, regardless of the actual type of item[itemKey]
.
<script setup lang="ts" generic="T, K extends keyof T">
defineProps<{
items: Array<T>
itemKey: K
}>()
</script>
<template>
<ul>
<li v-for="item in items" :key="String(item[itemKey as K])">
<slot :item="item" />
</li>
</ul>
</template>