ディレクトリ単位でTypeScriptの自動補完を制御する
前提条件
- TypeScriptを使用している
-
package.json
は1つのみ- 今回対象とするディレクトリのルートに
package.json
があり、それより子のディレクトリにはpackage.json
がない - モノレポではなくモノリスのイメージ。モノレポであれば恐らくworkspaceを切る方が何かと素直になりそう
- 今回対象とするディレクトリのルートに
試した環境
-
Visual Studio Code
- version 1.91(2024年7月7日頃の最新バージョン)
やりたいこと
- ディレクトリ毎に依存方向を決めている場合等に、依存してはいけないディレクトリの関数や変数が自動補完の対象となるのを防ぎたい
今回検証するリポジトリの構成
ルートディレクトリにpackage.json
とtsconfig.json
がある(ここは通常通り)
srcの直下にはconstants
, utils
, features
というディレクトリがある
features
→utils
→constants
という依存方向になっている
-
features
はutils
とconstants
をインポートできる -
utils
はconstants
をインポートできる -
constants
はsrc
配下の他のディレクトリはインポートできない
src/
constants/
utils/
features/
package.json
tsconfig.json
ディレクトリ毎に自動補完を制御したい場合は、tsconfigのexcludeを利用して制御する
1. ルートディレクトリのtsconfigは通常通り記述する
ルートディレクトリに置くtsconfig.json
は通条通り各種設定を記載する
// includeとexclude以外は今回の主旨と関係ない
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
2. 自動補完を制御したいディレクトリにtsconfigを追加し、excludeのみ上書きする
-
features
は全てのディレクトリからインポートが可能なので、自動補完の制御は不要 -
constants
とutils
ディレクトリは自動補完を制御したい
この場合はconstants
とutils
の直下にtsconfigを追加する
// src/constants
{
"extends": "../../tsconfig.json",
"exclude": ["node_modules", "../utils", "../features"]
}
// src/utils
{
"extends": "../../tsconfig.json",
"exclude": ["node_modules", "../features"]
}
基本的にはルートディレクトリのtsconfig.json
をそのまま継承し、excludeに依存したくないディレクトリを追加する
継承先で記述した項目は、継承元とマージされるのではなく上書きされるので、ルートでexclude指定していたnode_modules
はサブディレクトリに追加するtsconfig.json
にも記載する
constantsとutilsディレクトリで何が起きているか
- 現在のディレクトリからルートに向かって
tsconfig
を探しに行くので、constants
とutils
ではそれぞれのディレクトリに置いたtsconfig.json
の設定が適用される -
excludeはinclude設定の結果を変更する
- ルートディレクトリでは全ての
ts
,tsx
を対象としているが、constants
ではutils
とfeatures
を除外、utils
ではfeatures
を除外していることになる
- ルートディレクトリでは全ての
3. tsconfigをサブディレクトリに配置した後の挙動を確認
1. featuresではconstantsとutilsのモジュールが自動補完に出てくるのを確認
constantsの変数が自動補完に出てくる
featuresの変数が自動補完に出てくる
2. utilsではconstantsのモジュールは自動補完に出てくるが、featuresのモジュールは自動補完に出てこない
constantsの変数が自動補完に出てくる
featuresの変数が自動補完に出てこない
3. constantsではutilsとfeaturesのモジュールが自動補完に出てこない
utilsの変数が自動補完に出てこない
featuresの変数が自動補完に出てこない
constantsの変数は自動補完に出てくる(一応確認)
バッチリ期待取り自動補完が効いている
(番外編1)ディレクトリ関係なく自動補完を制御したい場合は、settings.jsonのautoImportFileExcludePatternsを使用する
vscodeに限った話だが、settings.json
に追加するだけで自動補完の対象外にすることが出来る。補完によく出てくるが全く使わないモジュール、ライブラリを直接使わずにラップした関数を使ってもらいたい場合などに有効
{
"typescript.preferences.autoImportFileExcludePatterns": [
"**/node_modules/@testing-library/react"
]
}
こちらはTypeScript4.8で登場した機能
(番外編2)明確な禁止はeslint-plugin-importのno-restricted-pathsで制御する
tsconfig
で変数を書いている際の自動補完の制御はできて便利だが、import文を書いている時は補完が効いてしまうし、vscode上でエラーも表示されない
今回は割愛するが、eslint-plugin-importのrestricted-pathsを使えば、imoprt側でリントエラーを出すことが出来る
兄弟ディレクトリで依存関係をもたせたくない場合などより細かいルールが設定できる
参考
終わりに
eslint-plugin-imoprt-accessをdefaultImportability: "package"
で設定し、ディレクトリ外からのアクセスを原則不可にすれば、変数名を狭いスコープで考えることが出来て良いのではないかと思って導入したところです。
そうすると今度は同じ変数名が自動補完にたくさん出てくる可能性が上がるのではないかと思い、何か良い方法はないかと考えたところ、この方法に辿り着きました。
ワークスペースを切ってしっかりとした依存関係を作るのが正攻法だと思いますし、この方法を取り入れるデメリットもあるかもしれませんが、ビルドやデプロイのことは特に考える必要なく簡単に導入でき、簡単に剥がすことが出来るのも魅力だと思っています。
検証で使用したコード
Discussion