TypeScript
NativeScript

NativeScript+TypeScriptで、未定義のJavaのクラスを呼び出す手順

NativeScriptでは、Androidの標準SDKは、 tns-platform-declarations をインストールすることで呼び出すことができますが、標準SDKでないものはエラーとなります。

例えば、

com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();

を使いたいとします。そのままIDEなどで以下のように記述すると...

app_init_service_android_ts_-_xzshowcase_-____projects_NativeScript_xzshowcase_.jpg

「Cannot find name 'com'」というエラーが出ていますね。

NativeScriptでは、実行時は全てのJava APIを呼び出すことができるのですが、TypeScriptでコンパイルエラーになっているのです。そんなときは以下の手順で使えるようにできます。

パッケージをany宣言して使う (さくっと使いたいとき)

declare let com:any;

これだけです。comパッケージがany型として宣言されるので、コンパイルチェックに通るようになります。TypeScriptのチェックとか要らないからすぐに使いたいよ!というときにはオススメ。私もよく使います。ただし、後述するように多少の危険性もあるので、やっていることを理解してから使いましょう。

TypeScriptの定義ファイルを作成(少し面倒ですが王道)

firebase.d.ts
declare module com {
    module google {
        module firebase {
            module iid {
                export class FirebaseInstanceId {
                    static getInstance(): any;
                }
            }
        }
    }
}

名前空間を module 定義の入れ子で宣言し、使用するメソッド定義( getInstance )も追加しました。戻り値を any にしておくことで、この関数の戻り値に対してはTypeScriptのチェックが効かなくなります

定義ファイルを読み込みます

プロジェクトのルートにある references.d.ts に以下の定義を追加します。

references.d.ts
/// <reference path="./firebase.d.ts" />

呼び出す

準備ができました。
以下のように呼び出すことができるようになります。

let refreshedToken = com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();

どちらが良いのか?

最初の any 宣言方式に比べると、TypeScriptの定義ファイル作成のほうは、面倒に感じられると思います。

別ファイルで入れ子でパッケージを宣言したりしてかなり手間暇がかかります。自分のアプリで使うだけなら、ここまでやる必要は無いかもしれません。ただ、外部の人が使うライブラリやプラグインを作るときには、こちらの方式のほうが良いでしょう。利用できるAPIを明確に定義できますし、型のチェックが効く+IDE補完も効くので、利用者に安全に使ってもらうことができます。

逆に言うと、any 宣言してしまうと、メソッド名が間違えてようが引数の型が合ってなかろうがコンパイルが通ってしまい、実行時クラッシュの危険性がありますし、リファクタリングもしにくくなります。

例えば以下のように com.google.〜 の部分を com.googl と書いても、com を any宣言している場合にはなんのエラーも出ません。

let refreshedToken = com.googl.firebase.iid.FirebaseInstanceId.getInstance().getToken()
//              ↑通ってしまう

TypeScriptの定義ファイルを作成している場合には、 googl というモジュールは無いよ!というコンパイルエラーとなり、事前にエラーに気づくことができます。

メリット・デメリットを理解してから使いましょう。
Enjoy NativeScript!