インターフェースとinstanceof
インターフェースはTypeScriptで独自に定義された概念であり、JavaScriptには存在しません。つまりコンパイルをかけると消えてなくなります。そのため他の言語でできるようなその型が期待するインターフェースかどうかの判定ができません。上記のStudentインターフェースで次のようなことをしても実行することはできません。
tsif ('Student' only refers to a type, but is being used as a value here.2693'Student' only refers to a type, but is being used as a value here.studentA instanceof) { Student // ...}
tsif ('Student' only refers to a type, but is being used as a value here.2693'Student' only refers to a type, but is being used as a value here.studentA instanceof) { Student // ...}
これを解消するためには型ガードを自前で実装する必要があります。以下はその例のisStudent()です。
tstypeUnknownObject <T extends object> = {[P in keyofT ]: unknown;};functionisStudent (obj : unknown):obj isStudent {if (typeofobj !== "object") {return false;}if (obj === null) {return false;}const {name ,age ,grade } =obj asUnknownObject <Student >;if (typeofname !== "string") {return false;}if (typeofage !== "number") {return false;}if (typeofgrade !== "number") {return false;}return true;}
tstypeUnknownObject <T extends object> = {[P in keyofT ]: unknown;};functionisStudent (obj : unknown):obj isStudent {if (typeofobj !== "object") {return false;}if (obj === null) {return false;}const {name ,age ,grade } =obj asUnknownObject <Student >;if (typeofname !== "string") {return false;}if (typeofage !== "number") {return false;}if (typeofgrade !== "number") {return false;}return true;}
以下はisStudent()の解説です。
戻り値のobj is Student
Type predicateと呼ばれる機能です。専門に解説してあるページがありますので参照ください。ここではこの関数が戻り値としてtrueを返すと、呼び出し元では引数objがStudentとして解釈されるようになります。
📄️ 型ガード関数
UnknownObject
typeofで判定されるobject型はオブジェクトではあるものの、プロパティが何も定義されていない状態です。そのためそのオブジェクトがどのようなプロパティを持っているかの検査すらできません。
tsconstobj : object = {name : "花子",};Property 'name' does not exist on type 'object'.2339Property 'name' does not exist on type 'object'.obj .; name
tsconstobj : object = {name : "花子",};Property 'name' does not exist on type 'object'.2339Property 'name' does not exist on type 'object'.obj .; name
そこでインデックス型を使っていったんオブジェクトのいかなるプロパティもunknown型のオブジェクトであると型アサーションを使い解釈させます。これですべてのstring型のプロパティにアクセスできるようになります。あとは各々のunknown型のプロパティをtypeof, instanceofで判定させればこの関数の判定が正しい限りTypeScriptは引数が期待するStudentインターフェースを実装したオブジェクトであると解釈します。
関数の判定が正しい限りとは
インターフェースに変更が加わった時この関数も同時に更新されないとこの関係は崩れてしまいます。たとえばstudent.nameは現在string型ですが、これが姓名の区別のために次のようなオブジェクトに差し替えられたとします。
tsinterfaceName {surname : string;givenName : string;}
tsinterfaceName {surname : string;givenName : string;}
この変更に対しisStudent()も随伴して更新されなければこの関数がStudentインターフェースであると判定するオブジェクトのnameプロパティは明らかに違うものになるでしょう。