Vue3.3の新機能とは? ジェネリクスコンポーネントを中心にdefineSlots()などの新機能を解説【後編】
TypeScriptのサポートがアップデート! Vue3.3の新機能まとめ 後編
Vue3、すなわち、Vue.jsのバージョン3がリリースされたのが3年前の2020年9月です。そのVue3が、Vueプロジェクトのデフォルトになったのが、2022年2月です。その時点でのVue本体のバージョンは3.2です。それから1年以上が経過した2023年5月11日に、Vue3.3がリリースされました。本稿では、前編と後編の2回にわたって、このVue3.3での新機能をまとめて紹介します。後編である今回は、ジェネリクスコンポーネントを中心に、defineSlots()などの他の新機能を紹介していきます。
ジェネリックコンポーネント
前編で紹介した新機能に続いて、次に紹介するのは、新機能リストの5です。これは、scriptタグに新たに導入されたgeneric属性についてです。具体的なコードを見る前に、少しサンプルを紹介したいと思います。
各組の人数を表示するサンプル
例えば、図1のような画面を考えてみます。
これは1〜6組の各クラスの人数とA〜F組の各クラスの人数を表示した画面です。この画面の元データとして考えられるのがリスト1の(1)と(3)の2種のMapオブジェクトです。
<script setup lang="ts">
:
const classSizeNoInit = new Map<number, number>();
classSizeNoInit.set(1, 35);
classSizeNoInit.set(2, 34);
:
const classSizeNo = ref(classSizeNoInit); // (1)
const focusedClassNo = ref(4); // (2)
const classSizeCharInit = new Map<string, number>();
classSizeCharInit.set("A", 35);
classSizeCharInit.set("B", 34);
:
const classSizeChar = ref(classSizeCharInit); // (3)
const focusedClassChar = ref("B"); // (4)
</script>
<template>
<section>
<h2>1〜6組の人数</h2>
<ClassSizeList v-bind:classSizeList="classSizeNo" v-bind:focused="focusedClassNo"/> // (5)
</section>
<section>
<h2>A〜F組の人数</h2>
<ClassSizeList v-bind:classSizeList="classSizeChar" v-bind:focused="focusedClassChar"/> // (6)
</section>
</template>
リスト1の(1)は、組の識別子が1組、2組、…、のような数字になっています。一方で、(3)は、A組、B組、…、のように文字になっています。それに合わせて、Mapのキーのデータ型は、(1)の元データとなるclassSizeNoInitではnumber型を、(3)の元データであるclassSizeCharInitではstring型としています。同様に、図1で「注目!!!」と表示させる組を表すリアクティブ変数である(2)と(4)に関しても、(2)はnumber型であり(4)はstring型となっています。
そして、これらのデータを子コンポーネントであるClassSizeListにPropsで渡して、図1のようなレンダリングにしたいとします。それが、(5)と(6)です。Prop名は、それぞれclassSizeListとfocusedです。
ユニオン型Props定義の問題点
では、このClassSizeListコンポーネントのProps定義はどのようにすればよいのかというと、まず思いつくのはユニオン型の定義です。これは、リスト2のようになります。
type Props = {
classSizeList: Map<number | string, number>;
focused: number | string;
};
これでとりあえずは動作します。ただし、問題があります。それは、例えば、リスト1の(5)において、v-bind:focusedとして渡さなければならない値、すなわち数値型であるはずのfocusedClassNoに「C」のような文字を渡しても、問題なく動作してしまうということです。コード上もエラーになりませんし、レンダリング結果でもエラーになりません。ただ、「注目!!!」の表記がなくなるだけです。この種類のバグは非常に発見しにくいです。
generic属性によるジェネリックコンポーネント
そこでVue3.3で新たに導入されたのが、ジェネリックコンポーネント、すなわち、scriptタグのgeneric属性です。これを利用したClassSizeListコンポーネントは、リスト3のコードとなります。
<script setup lang="ts" generic="CN"> // (1)
type Props = {
classSizeList: Map<CN, number>; // (2)
focused: CN; // (3)
};
defineProps<Props>();
</script>
リスト3の(1)では、scriptタグにgeneric属性としてCNを定義しています。この記述でいわゆるジェネリクスとして機能するようになります。もちろん、ジェネリクスですので、CNという記述は、なんでもよく、TでもTypeでもかまいません。
そして、このgeneric属性で指定した文字列をコンポーネント内では型指定として利用できます。(2)ではPropsのclassSizeListであるMapオブジェクトのキーとしてCN型を指定しています。同様に、Propsのfocusedも同じCN型となっています。こうすることで、classSizeListであるMapオブジェクトのキーのデータ型とfocusedのデータ型が同一でなければならなくなります。
そして、このCN型がどのようなデータ型になるかは、このコンポーネントを利用する側から渡されるPropsのデータ型から自動判定されるようになっています。例えば、リスト1の(5)では、Mapのキーとしてnumber型を指定したclassSizeNoをPropsのclassSizeListとして渡しています。この時点でCNがnumberと判定され、Propsのfocusedもnumber型である必要がでてきます。仮に、これに反して文字列を渡すとすると、図2のようにコーディング段階でエラー表示してくれます。
もちろんリスト1の(6)のCNがstringとなるパターンにおいても、focusedにnumber型の値を渡すと、同様にエラーとなります。実行してもエラーとならずにバグとなるユニオン型でのProps定義と比べると、コーディング段階からエラー表示となるこのジェネリクスコンポーネントとでは、安心感に雲泥の差がありますね。
この記事は参考になりましたか?
- ますます便利になるVue.jsの新機能を探ろう!連載記事一覧
-
- Vue.js3.4~3.5の新機能をまとめて紹介! 新しいAPIやSSRの改善
- Vue3.3の新機能とは? ジェネリクスコンポーネントを中心にdefineSlots()な...
- TypeScriptのサポートが強化されたVue3.3の新機能とは?──PropsとEmi...
- この記事の著者
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
