Support passing generic to child components
What problem does this feature solve?
We can pass type parameter in TypeScript via generics:
function <T extends string | number>doSth(value:T): T[] {
if (value === "123") {
return toArray(123); // throw an error, number[] is not compatible with T[]
}
return toArray<T>(value)
}
But we cannot do so in Vue template.
<my-select v-model="selectedFruit">
<my-option value="๐" />
<my-option value="๐" />
<my-option value="๐" /> // should throw an error, ๐ is not compatible with selectedFruit
</my-select>
To make this possible, we need the ability to restrict what components can be in slot๏ผ
ts defineSlots({ default: { accept: MyOption<T> } })
vue <my-select v-model="selectedFruit"> <my-option value="๐" /> <my-option value="๐" /> <my-option value="๐" /> // ๐ is not compatible with selectedFruit </my-select>
also, now we have ability to prevent developer from putting unexpected components in slots!
vue <my-select v-model="selectedFruit as any"> <my-radio v-model="radioValue" /> // throw an error, my-radio should not be here </my-select>
Sometimes TypeScript cannot infer acutal type we want:
<my-button-group :buttons="['๐', '๐']" @click="handleUpdate" />
type Foods = '๐' | '๐';
const handleClick= (food: Foods) => {
console.log(food);
}
buttons
will infer as string[]
, and @click
handle is (value: string) => void
, but we need Foods[]
and (value: Foods) => void
So we also need ability to specify what type we want:
<my-button-group[Foods] :buttons="['๐', '๐']" @click="handleUpdate" />
What does the proposed API look like?
For template:
<my-select[typeof selectedFruit] v-model="selectedFruit">
<my-option value="๐" />
<my-option value="๐" />
<my-option value="๐" /> // ๐ is not compatible with selectedFruit
</my-select>
<my-select v-generics="typeof selectedFruit" v-model="selectedFruit">
<my-option value="๐" />
<my-option value="๐" />
<my-option value="๐" /> // ๐ is not compatible with selectedFruit
</my-select>
For slot type definition:
defineSlots({
default: {
accept: MyOption<T>
}
})
This is an issue that requires consideration. In my opinion, using v-generic
(in line with <script setup lang="ts" generic="T">
) would be preferable. It's not feasible to add generics to the element tag as we need to maintain compatibility with native HTML code.
For slots, see https://github.com/vuejs/core/pull/7982#issuecomment-1493923997
/cc @johnsoncodehk