iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

TypeScriptプロジェクトでディレクトリ単位のカプセル化をする

こんにちは、株式会社iimonでエンジニアをしている遠藤です。 本記事はアドベントカレンダー7日目の記事となります。

TypeScriptでは、ファイルがプロジェクト内でカプセル化できる最大の単位になります。

そのため、ファイルでexportされていない変数や関数等は同じファイル内でしか読み込めず、また、exportされている変数や関数等に関してはプロジェクト全体で参照できるようになります。

ただ、プロジェクト全体から参照したくない場合もあるかと思います。 例えば、Reactで以下のように、同じディレクトリのコンポーネントを参照するコンポーネントがあるとします。

Drink/Drink.tsx

import DrinkItem from './DrinkItem'

export const Drink = () => {
  return (
  <div>
    <p>飲み物一覧</p>
      <div>
        {DRINK_LIST.map((drink, index) => (
          <DrinkItem 
            key={index}
            name={drink.name}
            price={drink.price}
          />
        ))}
      </div>
  </div>
)
}

この場合、DrinkItem.tsxはプロジェクト全体から参照することが可能ですが、使用したいのはDrinkディレクトリ内のみなので、他のディレクトリからは参照したくありません。

しかし、カプセル化を適切にするためにコンポーネントを分割しない場合、1つのファイルが肥大化し、コードを維持していきにくくなる場合もあるかと思います。

そこでeslint-plugin-import-accessというプラグインを使っている例を見かけたので、試してみます。

使い方

導入は簡単で、インストール方法についてはリポジトリのREADMEに記載の通りです。
https://github.com/uhyo/eslint-plugin-import-access

あとはディレクトリの外で呼び出したくないコンポーネント@packageをつけるだけです。

DrinkItem.tsx

import styles from './Drink.module.css'

...
/**
 * @package
*/
export const DrinkItem: React.FC<DrinkItemProps> = ({name, price}) => {
...

すると、導入後はディレクトリの外からDrinkItemを呼び出したときにeslintのエラーが表示されて、importできないようになります。

導入前

導入後

ディレクトリ内では変わらずimportができます。

ちなみに、named exportをdefault exportにすると挙動が上手くいきませんでした。(名前がないから?)

/**
* @package
*/
const DrinkItem: React.FC<DrinkItemProps> = ({name, price}) => {
...
}
export default DrinkItem;

普通にimportできる。。。

まとめ

今回はReactのコンポーネントを例に出しましたが、それ以外の場面でもテストのために関数を外出ししたいときやindex.tsからre-exportして参照するようにルール化しているプロジェクトなどで直接参照の制御をするなど役立つ場面が多そうだなと思いました。

参照したサイト

https://zenn.dev/uhyo/articles/eslint-plugin-import-access

最後に

この記事を読んで興味を持って下さった方がいらっしゃればカジュアルにお話させていただきたく、是非ご応募をお願いします。

Wantedly / Green

次のアドベントカレンダーの記事は引き続き遠藤が担当します!