インターフェースとinstanceof
インターフェースはTypeScriptで独自に定義された概念であり、JavaScriptには存在しません。つまりコンパイルをかけると消えてなくなります。そのため他の言語でできるようなその型が期待するインターフェースかどうかの判定ができません。上記のStudent
インターフェースで次のようなことをしても実行することはできません。
ts
if ('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 // ...}
ts
if ('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()
です。
ts
typeUnknownObject <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;}
ts
typeUnknownObject <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
型はオブジェクトではあるものの、プロパティが何も定義されていない状態です。そのためそのオブジェクトがどのようなプロパティを持っているかの検査すらできません。
ts
constobj : object = {name : "花子",};Property 'name' does not exist on type 'object'.2339Property 'name' does not exist on type 'object'.obj .; name
ts
constobj : 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
型ですが、これが姓名の区別のために次のようなオブジェクトに差し替えられたとします。
ts
interfaceName {surname : string;givenName : string;}
ts
interfaceName {surname : string;givenName : string;}
この変更に対しisStudent()
も随伴して更新されなければこの関数がStudent
インターフェースであると判定するオブジェクトのname
プロパティは明らかに違うものになるでしょう。