なぜ default export を使うべきではないのか?

フロントエンド開発センター(通称: UIT)の花谷(@potato4d)です。

この記事では、 ESModule の仕様であり、現在の JavaScript を用いた開発で必須である import / export 構文について、 default export ではなく named export を利用すべきである理由について紹介します。

前提知識について

なお、本記事は以下を前提知識として、詳細の説明を省きます。

  • ESModule と import / export についての基本
  • CommonJS module との違い
  • esModuleInterop 周り(CJS → ESM の import については扱わないため)

プロジェクトで使われている技術と ESModule の状況について

UIT では、 SPA 開発のプロジェクトにおいて Vue.js と React が多く利用されており、既存の多くは Babel を利用した JavaScript で、新規のプロジェクトでは TypeScript を利用して開発が行われています。

FYI: 【LINE DEV DAY 2019 番外編】UIT Front-end Tooling Survey 2019

技術選定は勿論、プロジェクトにおける細かなコーディングルールについては、プロジェクトのコードオーナーに委ねられており、プロジェクトごとに裁量を持った意思決定を行っています。

その上で、私が携わるプロジェクトにおいては、 default export を可能な限り避けるように心がけています。

なぜ default export を使うべきではないのか?

なぜ default export を利用すべきではないのか。

default export は CommonJS 時代の module.exports を彷彿とさせる仕様であり、考えることも少なく便利な仕様であるはずです。

事実私も default export を頻繁に利用していましたが、大きな2つの課題によって named export のみを許容するようになりました。

それは課題がリファクタリングのしやすさとエディタとの親和性という、プロジェクトの生産性に影響を及ぼすものであったからです。

それぞれの項目について詳しく紹介します。

import 側の裁量で対象を自由に命名できてしまう

今回は「『Date から ISO8601 に変換していたコード』があとから『YYYY/MM/DD』形式になった」というシチュエーションを想定したいと思います。実際には YYYY/MM/DD から YYYY/MM/DD hh:mm:ss あたりになることが多いと思いますが、サンプルコードの簡略化のためにこの仕様とします。

このシチュエーションにおいて、default export を利用し、src/main.ts から src/modules/converter.ts を読み込んだコードは以下です。

// src/main.ts
 
import convertToISO8601 from './modules/converter'
 
async function run(startAt: Date) {
  console.log(
    convertToISO8601(startAt)
  )
}
 
run(new Date())
// src/modules/converter.ts
 
export default function (date: Date) {
  return date.toISOString()
}

一見良いコードに見えます。

しかし、これがあとから convertToDate がふさわしい実装になってしまった場合はどうでしょうか。

src/main.ts では convertToISO8601 という名称が名付けられていますが、これは import 側が自由に設定した名前となります。

src/component/Foo.ts では toISO8601 として import されているかもしれませんし src/components/Bar.tsx では Date にあわせて toISOString として import されているかもしれません。

プロジェクトにおいて、表示フォーマット一つとっても、アプリケーション全体で統一したいがあとから変わる可能性がある仕様というのは存在します。仕様だけではなく、責務が広がって関数名が適切ではなることは開発において頻繁にあります。それがメンテナンスフェーズである場合は、特に頻発するのではないでしょうか。

単純に初期から仕様を統一というだけで決まる話ではありませんし、何よりも後からやるとその影響範囲は膨大。置き換えようにもデグレしていないかをすべて確認することもコストになります。

こういった軽い負債ではあるものの、アプリケーションの複雑さが増すような割れ窓を作る余地が default export にはあります。

named export を利用した変数名の固定化

このような問題が、 named export で解決できます。
具体的には、以下のように必ず名前付きで export するようにし、 * as は使わずに、利用する関数のみを import します。

// modules/foo.ts
 
import { convertToISO8601 } from './bar'
 
async function run(startAt: Date) {
  console.log(
    convertToISO8601(startAt)
  )
}
 
run(new Date())
// modules/bar.ts
 
export function convertToISO8601(date: Date) {
  return date.toISOString()
}

こうすることで、プロジェクト全体に s/convertToISO8601/convertToDateTimeString/g の形での置換を行うだけで、ランタイムエラーもコンパイルエラーも起こさずにリファクタリングが完了します。

この小さな差異は、特に将来性のあるプロジェクトでは、コードベースが肥大化すればするほど効いてくる違いとなります。

エディタの自動 import が named export でのみ効果を発揮する

また、 named export の場合、ファイル名が一意に定まっているため自動 import の恩恵を最大限受けることができます。

フロントエンド開発では、 TypeScript の開発元でもある Microsoft によって作られている製品ということもあり、 VSCode でコーディングを行っている人が大半ではないかと思います。

例えば VSCode の場合、以下のように完全一致の名称を入力することで該当する export された変数・関数・Type・Interface・Classなどすべてを補完の上で import することができます。

これは import path を調べる過程を省くことができるほか、 import の typo を省くことができる、自動的に分割した import となるため、不要なコード上の依存が発生しないというメリットもあります。

生産性という観点においても、 named export は default export と比較して優位な点が豊富にあります。

named export のみとする場合のポイント

ここまでで default export を利用せずに named export を利用する利点を紹介しました。あわせて named export を利用する場合のポイントについていくつか紹介します。

import 時の as も極力利用を避ける

default export においてリファクタリングのしやすさという観点では、あまり as を利用しないということも大切です。

Server-side Node.js プロジェクトの場合は Express が Request や Response といった主語の大きな名前空間を専有しているせいで { Request as ExpressRequest } などとせざるを得ないシチュエーションは頻発しますが、それ以外のシチュエーションでは基本的にライブラリとの競合も発生しないはずです。

as を利用しないことで、愚直な置換でも命名被りが起きないことが保証されているため、安心してリネームとリファクタリングをすすめることが可能です。

ファイル名を明確なものにしておく

もう一点、こちらはエディタとの親和性の文脈となりますが、 utils/converter/index.ts といった命名よりも、 utils/textFormat/converter.ts としておくとベターです。

こうすることで、 Auto Import される時及びそれを遡るときによりわかりやすくトラッキングすることができます

おわりに

今回は JavaScript/TypeScript 環境において、 default export ではなく named export を行うべき理由について紹介しました。

LINE株式会社のUITでは、このような技術的な議論を日々行っています。

もっとLINEについて知りたい場合は、カジュアル面談も積極的に行っておりますので、ぜひご連絡ください。

求人情報: フロントエンドエンジニア / フロントエンド開発センター

Related Post