$ ryokkkke.com/TypeScript/tsconfig.json

paths

概要

https://www.typescriptlang.org/tsconfig#paths

{
  "paths": {
    "任意の文字列/*": ["path/to/first/*", "path/to/second/*"]
  }
}

import する際に記載するパスにエイリアスをつけることができます。

このオプションを指定する場合、baseUrlの指定が必須となります。
pathsに指定するパスは、baseUrlに指定したパスからの相対パスになります。

また、任意の文字列 1 つに対して複数のパスを指定することができます。
その場合、配列の先頭のパスから順に存在をチェックし、見つかった段階でモジュールを返します。

詳細

自前の TS ファイルをimportする際によく起きるのが、相対パスがやたらと長くなる問題です。
(TS に限った話ではなく JS 全般の問題ですが)
特に自身が配置されているディレクトリよりも上の階層のディレクトリのファイルを参照しようとすると、../が連続し、かなり見にくくなります。

import Hoge from "../../../../../hoge";

こういう感じの。

pathsは、よく使うパスにエイリアス(別名)をつけておくことでこうした見づらいimport文を見やすくできるオプションです。

例えば React のレポジトリで、 src/components/ 配下にコンポーネントを置いていたとすると、色々な場所でこのパスからファイルをimportすることになるでしょう。
その場合は、例えば下記のようにcomponentsというエイリアスをつけます。

{
  "baseUrl": "./",
  "paths": {
    "components/*": ["src/components/*"]
  }
}

このpathsに指定しているオブジェクトのキー側("components")がエイリアスで、バリューの配列に入っている文字列("src/components")が実際のパスになります。
エイリアスは任意の文字列で良いので、実際のパスと文字列が同じである必要はありません。
ディレクトリにエイリアスをつける際は、双方の末尾に/*が必要なことに注意してください。

この時、baseUrlの指定が必須となります。

baseUrlは相対パスでimportする際の基準となるディレクトリを指定するオプションですが、pathsで指定するパスもこのbaseUrlで指定したディレクトリが基準となります。

src/
├── components
│   ├── hoge.tsx
│   └── dir1
│       └── dir2
│           └── dir3
│               └── piyo.tsx
└── index.ts

↑ 例えばこういう階層だった場合、piyo.tsxからhoge.tsxを普通にimportしようとすると

import { TopComponent } from "../../../hoge";

↑ こうなりますが、先ほどpathsでエイリアスをつけたので、同じimport文を

import { TopComponent } from "components/hoge";

↑ こう書くことができます。

ただし、コンパイル後にパスの置換が必要

便利なんですが、実際これだけでコンパイルすると、ランタイムエラーになります。

実際先ほどの例のpiyo.tsxをコンパイルした JS は、以下のように、components/hogeがそのまま書き出されており、../../../hogeになっていません。

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
exports.PiyoComponent = void 0;
var react_1 = __importDefault(require("react"));

var hoge_1 = require("components/hoge"); // <- ここ

exports.PiyoComponent = function () { return (react_1["default"].createElement("div", null,
    react_1["default"].createElement("div", null, "I'm at src/components/hoge/piyo/fuga/."),
    react_1["default"].createElement(hoge_1.HogeComponent, null))); };

この状態だと、実行時に Cannot find module 'components/hoge'みたいな感じでエラーになります。
これは、TypeScript が実際のコンパイル時にパスの書き換えを行ってくれないからです。

TypeScript の paths はパスを解決してくれないので注意すべし! – 自主的 20%るぅる
こちらの記事でも紹介されていますが、

Module path maps are not resolved in emitted code · Issue #10866 · microsoft/TypeScript

この挙動に関してはここで大激論が交わされているのを確認することができます。

仕方ないので webpack で置換します。
ts-loaderを使った例は上で紹介した方の例を見てみてください。ここでは next.config.jsの中身を置いておきます。

module.exports = {
  webpack(config, options) {
    // webpack上でも同じパスを指定
    config.resolve.alias["components"] = "src/components";

    return config;
  },
};

webpack の設定のresolve.aliasが、TS の paths と全く同じ意味合いの設定になります。


関連オプション:baseUrl