かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

ts-node で path alias が効かないにハマる

TypeScript な Express を ts-node で動かしてて tsconfig.json に path alias を設定したんだけどエラーになってなんもわからん。ってなったのでメモ

環境
  • typescript: ^4.5.4
  • ts-node: ^10.4.0
  • ts-node-dev: ^1.1.8

結論

tsconfig-paths を使って ts-node 実行時に -r tsconfig-paths/register オプションを渡してあげれば OK

package.json (entry point が src/index.ts の場合)

{
  "scripts": {
    "start": "ts-node -r tsconfig-paths/register src/index.ts",
    "dev": "ts-node-dev -r tsconfig-paths/register src/index.ts",
    "build": "tsc"
  }
}

tsconfig.json

{
  "compilerOptions": {
    // …
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    }
}

/src 内のファイルを @/ で import できるようにパスエイリアスを設定した

npm script

package.json

{
  "scripts": {
    "start": "ts-node src/index.ts",
    // … 
  }
}

ts-node で path alias が効かなくてエラーになる

$ npm run start
> server@1.0.0 start
> ts-node src/index.ts
[ERROR] 23:53:57 Error: Cannot find module '@/data/xxx'

パスエイリアスを使っている module のところでエラーになる
なんもわからん…

ts-node は単体では path alias に対応してないっぽい

That's the node.js error - TypeScript is working fine, but node will never be able to resolve an alias like that unless TypeScript emits code that rewrites paths.
cf. tsconfig.json/paths not working with ts-node · Issue #138 · TypeStrong/ts-node · GitHub

tsconfig-paths を使う

ts-node -r tsconfig-paths/register として呼び出せば ts-node でも path alias が効くようになるっぽい!

$ npm i -D tsconfig-paths

package.json を書き換える

{
  "scripts": {
-   "start": "ts-node src/index.ts",
+   "start": "ts-node -r tsconfig-paths/register src/index.ts",
    // … 
  }

再実行

$ npm run start
> server@1.0.0 start
> ts-node src/index.ts

Start on port 4000.

₍ ᐢ. ̫ .ᐢ ₎👌 解☆決

tsconfig.json に設定する方法もある

typescript-transform-paths を使えば設定を tsconfig.json にまとめられて npm script で -r tsconfig-paths/register オプションを渡さなくてもOKになる

cf. tsconfig.json/paths not working with ts-node · Issue #138 · TypeStrong/ts-node · GitHub

$ npm i -D typescript-transform-paths

package.json

{
  "scripts": {
-   "start": "ts-node -r tsconfig-paths/register src/index.ts",
+   "start": "ts-node src/index.ts",
    // … 
  }

tsconfig.json

{
+ "ts-node": {
+  "transpileOnly": true,
+  "require": [
+   "typescript-transform-paths/register",
+   "tsconfig-paths/register"
+   ]
+ },         
  "compilerOptions": {
    // …

👇

$ npm run start
> server@1.0.0 start
> ts-node src/index.ts

Start on port 4000.

₍ ᐢ. ̫ .ᐢ ₎👌


ts-node-dev での path alias

ts-node では開発時にファイルを監視してのホットリロードができません。以前は nodemon を使ってたのですが ts-node-dev の方が高速でナウでヤングらしいので導入してみました。

$ npm i -D ts-node-dev

ts-node-dev もデフォルトではパスエイリアスが効かない

ts-node-dev も ts-node と同様でデフォルトでは tsconfig.json に書かれているパスエイリアスが効かないので実行時に -r tsconfig-paths/register オプションを渡してあげる必要があります

package.json

{
  "scripts": {
   "start": "ts-node -r tsconfig-paths/register src/index.ts",
   "dev": "ts-node-dev -r tsconfig-paths/register src/index.ts"
    // … 
  }
}

👇

$ npm run dev
> server@1.0.0 dev
> ts-node-dev -r tsconfig-paths/register src/index.ts

[INFO] 00:55:03 ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.5.4)
Start on port 4000.

src/index.ts や import しているファイルを変更した際に自動的に再起動されればOK
₍ ᐢ. ̫ .ᐢ ₎👌👌👌

⚠ ts-node-dev では typescript-transform-paths を使って tsconfig.json にまとめる方法がうまく動作しない

ts-node と同じように tsconfig.json にパスエイリアスの設定をまとめられるかと思ったけど、うまく動作させられなかった

tsconfig.jsonts-node-dev のセクションを追加

{
  "ts-node": {
    // …
  },
+ "ts-node-dev": {
+  "transpileOnly": true,
+  "require": [
+   "typescript-transform-paths/register",
+   "tsconfig-paths/register"
+   ]
+ },         
  "compilerOptions": {
    // …

package.json の npm script からオプションを削除

{
  "scripts": {
   "start": "ts-node  src/index.ts",
-  "dev": "ts-node-dev -r tsconfig-paths/register src/index.ts"
+  "dev": "ts-node-dev src/index.ts"
    // … 
  }

👇 パスエイリアスが解決できない

$ npm run dev
> server@1.0.0 dev
> ts-node-dev src/index.ts

[INFO] 01:01:28 ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.5.4)
Error: Cannot find module '@/data/xxxx'[ERROR] 01:01:32 Error: Cannot find module '@/data/xxxx'

typescript-transform-pathsts-node-dev のセクションを作れないっぽいです。
もしかしたら tsconfig.jsoncompilerOptions 内で設定すればうまく動作させられるかもですが、そこまでは調べませんでした。

所感

typescript-transform-paths を使うとパスエイリアスの解決関連を tsconfig.json にまとめられて npm script の見た目はシンプルになるけど、開発で使うだろう ts-node-dev でいい感じに動作させられなかったし、パッケージも増えてしまうのでシンプルに ts-node, ts-node-dev 両方に -r tsconfig-paths/register オプションを渡してしまうのが良さそうだと思いました。

package.json

{
  "scripts": {
   "start": "ts-node -r tsconfig-paths/register src/index.ts",
   "dev": "ts-node-dev -r tsconfig-paths/register src/index.ts"
    // … 
  }
}

おわり。


[参考]

銀河

銀河

Amazon