飴屋

フロントエンド/ファイル構成

TypeScriptの設定ファイルの場所

node.jsのパッケージを既存のファイル構成の外側に置いたケースで、TypeScriptのファイルをwebpackしようとしたら「TS18003」エラーでコケました。

nodejs/
- package.json
- webpack.config.ts
- tsconfig.json
web/
src/
- main.ts

見慣れない構成になってしまったので、うまくいかなかったのはその辺に原因があると考えました。よくある構成のときは動いたし。エラーメッセージを読むと「tsconfig.json」の問題のよ、うでしたので何か記述を足す方がいいのかなと、しばらくいろいろ試しました。

解決方法は「tsconfig.json」の場所でした。

nodejs/
- package.json
- webpack.config.ts
web/
- tsconfig.json
src/
- main.ts

TypeScriptのentryファイルのある場所の一つ上の階層に「tsconfig.json」が配置されていると問題が解消しました。そういうものなのか。configファイルの場所は上の階層に向かって探しにいくらしいですが、変なファイル構成だとうまくまとめられませんでした。

よそのグローバル変数

本来、webpackは複数のファイルをまとめて一つにまとめることで、ネットワークの効率を高めるのに使うと思うのですが、複数にファイルを分割しないといけないケースがありました。

js/
- first.js
- second.js

まず読み込んでおいたfirst.jsの処理を受けて、ユーザーのリアクションがあって、それに応じてsecond.jsを読み込んで処理が始まるという構成になるのでした。そして今回second.jsの方をTypeScriptで書き直してwebpackでまとめるということになりました。そしてfirst.jsで生まれたグローバル変数hogeが、second.jsにも登場するのでした。

webpackでtsファイルをまとめようとすると[TS2304]エラーが出てsecond.jsができあがりません。まとめるtsファイルの中で変数hogeが宣言されていないので、登場しても名前が解決できないわけですね。webpackが、パッケージをその中身で完結させて、他のファイルに依存しないようにしたいというわけではなく、TypeScriptが外部由来のものはインポートするなりなんなりして、全て把握した状態でコンパイルしたいということのようです。first.jsでの宣言が先にあるので、second.jsでは勝手に別途宣言できないhogeですが、宣言しないとTypeScriptで扱えないという状況になってしまいました。

そういう時はアンビエント宣言をしろとのことでした。

declare var hoge;

declareがついたアンビエント宣言によって、生成されるJavaScriptには直接影響はないけど、宣言された変数がどんなものかコンパイラにはこれで伝わるようです。first.jsで生まれた変数なので、元々型情報がないから省略したけど、

declare var hoge:string;

本来は型情報を付けて宣言してあげて、リントなりデバッグなりに使うものなのかもしれません。とりあえずsecond.jsには影響のない形でコンパイルできるようになりました。アンビエントは「環境」みたいな意味かな。直接プログラムに出てくるわけではない環境設定情報みたいな感じですかね。変数以外に関数にもクラスにもdeclareは使えるとのこと。ファイルの先頭にでも書いておけばいいし、量が多ければ別途.d.ts拡張子のファイルを用意してまとめて書いておけるらしい。(宣言ソースファイル)

JavaScriptとTypeScriptがパッケージに同居

JavaScriptのプロジェクトをTypeScriptで書き直してwebpackでパッケージにしようとしていましたが、JavaScriptの中身の一部が権利関係の都合上、TypeScriptで書き直せないというケースになりました。こういうときはjsとtsでファイルを分けて、エントリーファイルであるtsファイルでjsをrequireで読み込めばいいらしい。

/src
- entry.ts
- kenri.js
const kenri = require("./kenri");

でも、↑これだとコンパイルできませんでした。

requireはnodejsに備わったjavascriptの装備なのでTypeScriptでは使えないのでした。でも、webpack(nodejsのモジュール)で今回はパッケージにするのでrequireは使える。typescriptのモジュールが未知の"require"という言葉に反応してコンパイルを止めちゃっているだけみたい。

declare function require(string): any;
const kenri = require("./kenri");

アンビエント宣言でrequire関数を認知させればいいらしい。