Vue 泛型元件,讓 TypeScript 更精確
大家好,我是鱈魚。(^∀^●)ノシ
Vue 3.3 終於新增了泛型元件(Generic Component),可以在 TypeScript 環境中得到更準確的型別提示了!ˋ( ° ▽、° )
這個泛型元件與 TypeScript 的 Generic 是同一個概念,不知道 Generic 的朋友可以先來來複習一下。
甚麼?你說你沒有使用 TypeScript?抱歉浪費您 5 秒鐘,可以上一頁了。(>人<;)
或者在離開前路過文件湊個熱鬧。( •̀ ω •́ )y
有使用 TypeScript 的朋友就讓我們一起繼續看下去吧。ლ(╹◡╹ლ)
來個 select
假設我需要一個綁定物件的 select,最常見的問題就是 modelValue 只能 any,如下:
SelectAny.vue
<script setup lang="ts">
...(省略其他程式碼)
interface Prop {
modelValue: any;
label?: string;
options: any[];
optionLabel?: (option: any) => string;
/** 是否多選 */
multiple?: boolean;
}
...
</script>
...
v-model 這個糖
忘記元件怎麼實現 v-model 的朋友們別擔心,讓我們複習一下。
v-model 只是一個語法糖,實際上的程式碼如下(文件)。
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
modelValue 就是用來表示預設綁定值的變數,讓我們回到剛剛的 any。
所以現在就算我們的 modelValue 與 options 的型別不同,或者 multiple 為 true,而 modelValue 卻沒有給矩陣時,都不會有任何錯誤提示。
<script setup lang="ts">
...
import SelectAny from './SelectAny.vue';
const options: User[] = [
{ name: '鱈魚', price: 10000 },
{ name: '章魚', price: 100 }
];
const anyDatum = ref<{ age: number }>();
const selectedAnyDatum = ref<User>();
</script>
<template>
<!-- anyDatum 與 option 型別不同 -->
<SelectAny v-model="anyDatum" :options="options" :option-label="getLabel" />
<!-- modelValue 沒有給矩陣 -->
<SelectAny v-model="selectedAnyDatum" multiple :options="options" :option-label="getLabel" />
</template>
畢竟是 any 嘛。(˘・_・˘)
加上泛型
如果加上元件泛型就不會有以上問題了。
SelectGeneric.vue
<script
setup
lang="ts"
generic="ModelValue, Multiple extends boolean"
>
...
interface Prop {
modelValue: Multiple extends true ? ModelValue[] : ModelValue;
label?: string;
options: ModelValue[];
optionLabel?: (option: any) => string;
/** 是否多選 */
multiple?: Multiple;
}
...
</script>
...
除了 modelValue、options 有泛型 ModelValue 外,我們還加上 multiple 判斷,如果為 true,則 modelValue 為 ModelValue,否則為 ModelValue。
這樣子就可以達成以下效果:
- modelValue 與 options 型別不同時警告,
- multiple 為 true 時,如果 modelValue 沒有提供矩陣時發出警告。
<script setup lang="ts">
...
import SelectGeneric from './SelectGeneric.vue';
const options: User[] = [
{ name: '鱈魚', price: 10000 },
{ name: '章魚', price: 100 }
];
const anyDatum = ref<{ age: number }>();
const selectedGenericDatum = ref<User>();
const selectedGenericData = ref<User[]>([]);
</script>
<template>
<!-- Type '{ age: number; }' is not assignable to type 'User | User[]'.ts(2322) -->
<SelectGeneric v-model="anyDatum" :options="options" :option-label="getLabel" />
<!-- Type 'User' is not assignable to type 'User[]'.ts(2322) -->
<SelectGeneric v-model="selectedGenericDatum" multiple :options="options" :option-label="getLabel" />
<!-- 正確 -->
<SelectGeneric v-model="selectedGenericData" multiple :options="options" :option-label="getLabel" />
</template>
TypeScript 成功重返光榮。o((>ω< ))o
文章到此結束了,感謝您的閱讀,以上程式碼可以在此取得。
有其他想法請不吝告訴我,鱈魚感謝您!(o゜▽゜)o☆
總結 🐟
- Vue 3.3 新增泛型元件
- 泛型元件可以讓 TypeScript 推導更精確