TypeScriptバージョン3から5.2までのアップデート内容まとめ ──型の絞り込みについて解説
ますます便利になるTypeScript! バージョン3からの変更点と総まとめ 第4回
その登場以来、順当にアップデートを重ねているTypeScriptに関して、バージョン3から5.2までの変更点をまとめて紹介するのが、本連載です。前回は、TypeScriptの根幹ともいえる型システムに関する変更点を紹介しました。今回は、型の絞り込みに関する変更点を紹介します。
制御フローと型の絞り込み
本連載は、TypeScriptのバージョン3から5.2までのアップデート内容を、テーマごとにバージョン横断で紹介する連載です。第4回である今回紹介するのは、型の絞り込みに関する変更点です。まず、その型の絞り込みの確認から話を始めていきます。
型の絞り込みとは
例えば、前回登場したPersonalBaseData型を値の型、数値をキーの型とするMapオブジェクトを用意し、リスト1のようなコードを考えたとします。
const list = new Map<number, PersonalBaseData>(); : const key = 22454; const item = list.get(key); const name = item.name; // (1)
このコードは、(1)で図1のエラーとなります。このエラーメッセージの通り、Mapオブジェクトからget()した要素は、引数のキーのデータによっては取得対象の値が存在せず、undefinedとなる可能性があるからです。そして、このundefinedな変数のプロパティへのアクセスはエラーを招くため、TypeScriptでは事前にエラー表示となります。
実際、get()とした値を格納した変数(リスト1ではitem)のデータ型をVS Codeで表示させると、図2のように、PersonalBaseDataとundefinedとのユニオン型となっています。
このエラーを解決するために、リスト1の(1)の部分は、リスト2のようにundefinedかどうかのチェックを行います。
if(item != undefined) {
const name = item.name;
}
そして、TypeScriptでは、このようにundefinedの可能性を排除した条件ブロック内では、その変数(リスト2ではitem)は、図3のように、undefinedの型がなくなり、単なるPersonalBaseData型となっています。
このように、if-else if-elseやswitchの条件分岐構文において、変数の型チェックを行うことを、「制御フロー分析(Control Flow Analysis)」といいます。そして、その制御フロー分析内で、条件に応じて型の可能性が排除されていくことを、「型の絞り込み(Type Narrowing)」といいます。また、型の絞り込みで行う条件部分(リスト2ならば「item != undefined」の部分)を「型ガード(Type Guards)」といいます。
型ガードの変数化
この型ガード、すなわち、型の絞り込み条件は、()内に記述して初めて、制御フローブロック内で型の絞り込みが有効になっていました。これが、変数化しても絞り込みが有効になるようにバージョン4.4で変更されています。これを利用すると、リスト2は、リスト3のようなコードになります。
:
const item = list.get(key);
const isNotUndefined = (item != undefined); // (1)
if(isNotUndefined) { // (2)
const name = item.name; // (3)
}
リスト3では、(1)で型ガードをisNotUndefinedと変数化しており、その変数isNotUndefinedを(2)の条件としています。それでも、型の絞り込みは行われ、(3)ではエラーが発生しません。
inによる型ガード
型ガードを行う場合、「!= undefined/null」の他に、JavaScript由来のtypeof演算子やinstanceof演算子を利用した型チェックコードも、もちろん型ガードとして利用できます。ただし、typeof演算子は、プリミティブ型のチェックは行えますが、オブジェクトは単にobjectとしてチェックできるだけで、どのオブジェクトかまではチェックできません。また、instanceofはオブジェクトをnewしたものとの比較となります。ということは、オブジェクトリテラルの型チェックはできません。
例えば、前回登場したPersonalBaseData型かPersonalBirthData型かの型ガードを行う場合は、typeofやinstanceofは使えず、代わりに、inを使います。inもJavaScript由来の演算子であり、そのオブジェクトに特定のプロパティが含まれているかをチェックする演算子です。これを利用すれば、リスト4のような制御フロー分析コードが記述できます。
function show(person: PersonalBaseData | PersonalBirthData): void {
const name = person.name; // (1)
if("phone" in person) { // (2)
const phone = person.phone; // (3)
:
}
else { // (4)
const birth = person.birth; // (5)
:
}
}
リスト4の関数show()の引数personはPersonalBaseData型かPersonalBirthData型かのどちらかです。そのどちらもnameプロパティは存在しているので、(1)のコードは問題なく動作します。一方、(3)のようなphoneプロパティへのアクセスはPersonalBaseData型でなければエラーとなります。同様に、(5)のようなbirthプロパティへのアクセスはPersonalBirthData型でなければなりません。そこで、(2)や(4)のように制御フロー分析を行って、それぞれのブロック内で型の絞り込みが行われた上でアクセスするようにしています。
その条件として利用しているのが、(2)のinです。(2)では、phoneプロパティがpersonに含まれているかを型ガード(型の絞り込み条件)としています。
未定義プロパティへのアクセスがinによって可能に
このinによる型ガードの仕組みが、バージョン4.9でより強力になりました。例えば、あるサーバAPIエンドポイントから取得したJSONデータを表すインターフェースとしてリスト5のReturnJSONを考えます。statusはサーバ側の処理が成功したかどうかを表す数値として1(成功)か0(失敗)が格納されています。そして、その失敗か成功かでresultのデータ内容が変わってくるため、unknown型となっています。
interface ReturnJSON {
status: number;
result: unknown;
}
ここで、statusが1、つまり成功した場合、resultに格納されたオブジェクトのmsgプロパティを取得するコードを考えます。そのような関数をextractResult()としたならば、これは、例えば、リスト6のようなコードとなります。
function extractResult(returnJSON: ReturnJSON): string {
let result = "通信失敗";
if(returnJSON.status == 1) {
let result = "データ取得失敗";
if(returnJSON.result != null && typeof returnJSON.result == "object" && "msg" in returnJSON.result && typeof returnJSON.result.msg == "string") { // (1)
result = returnJSON.result.msg; // (2)
}
}
return result;
}
リスト6で注目するのは、(2)です。ReturnJSON型のreturnJSONのresultプロパティは、リスト5の通り、unknown型です。となると、そのプロパティのmsgへのアクセスは、本来エラーとなるはずですが、リスト6ではなりません。その種明かしが、(1)の条件(型ガード)の「"msg" in returnJSON.result」の部分です。この条件に合致した時点で、resultプロパティ内にはmsgプロパティは存在すると判断し、図4のようにプロパティmsgが存在するオブジェクトとして扱われるようになります。
この記事は参考になりましたか?
- ますます便利になるTypeScript! バージョン3からの変更点と総まとめ連載記事一覧
-
- TypeScriptに導入された新たな仕組みのデコレータ、その使い方と利便性とは?
- プライベート構文やアクセサなど、TypeScriptのクラス構文に関する変更点を解説
- TypeScriptバージョン3から5.2までのアップデート内容まとめ ──型の絞り込みに...
- この記事の著者
-
WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、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編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
