すべてを String で書いてたら、あるときエラーになってしまったので共有します。
エラーの理由
クライアント側のコードをTypeScriptで書いていたとき、SocketIOのライブラリを使っていたところ、次のエラーになりました。
1 |
error TS2345: Argument of type 'String' is not assignable to parameter of type 'string'. |
ライブラリの定義を確認すると string の引数を受け取るところに String の変数を与えていることが分かりました。
Stringとstring? あれ、同じじゃないの?なんて思っていたらどうやら違うようです。
エラーの再現コードはこんな感じです。
1 2 3 4 5 6 7 8 9 |
public call() { let val1: String = 'val1'; this.callee(val1); // ここで error TS2345 } // この callee 関数はライブラリ内にある public callee(param: string ) { console.log('param: ', param); } |
string は TypeScript の型
結論は、 String はJavaScriptの文字列型であり、 string はTypeScriptの文字列型です。
型互換性があるのかと思えば、 string から String へは暗黙的なキャストが可能なのに対し、逆の String から string へは暗黙的なキャストができません。
コードでまとめると次のような形になります。
1 2 3 4 5 6 7 8 9 10 11 12 |
let a = new String('val'); // String let b: String = 'val'; // String let c = 'val'; // string let d: string = 'val'; // string let jsString: String; let tsString: string; jsString = tsString; tsString = jsString; // throws cast error |
厄介なのは、少なくとも執筆時点で私のIDE (IntelliJ IDEA) で、このエラーをコンパイル前に指摘してくれないことです。
(例えば上記のコードで tsString の型定義が number であれば、下2行ではどちらでもエラーとして指摘してくれます。)
型アサーション
いわゆる明示的なキャストです。
TypeScript では Type Assertions と呼ばれていて、これを使えば String 型から string 型への代入もできるようになります。
1 2 3 4 5 6 7 8 |
let jsString: String; // JavaScript String let tsString: string; // TypeScript String jsString = tsString; // OK tsString = jsString; // Error tsString = <string> jsString; // OK ・・・ 1 tsString = jsString as string; // OK ・・・ 2 |
公式のドキュメントには1の書き方しか記載がありませんが、去年の夏頃にas演算子が実装され、2の書き方ができるようになりました。
個人的には as を使った書き方の方が好みです。
まとめ
String と string が違う型であると気付けば修正できますが、気付かないとハマり、そして検索しにくい(大抵の検索エンジンは大文字小文字が区別されない)のでお気を付けください。。
TypeScriptで開発していく場合、基本的にはTypeScriptの文字列型である string に統一すれば良いかと思います。
所感
最近すっかり暖かくなりましたね。
今日外に出ると桜が満開で、去年の今頃を思い出し、1年の時間の早さにびっくりしました。