Next.js (TypeScript) で MUI (Material UI) を型安全に使用する

MUIの公式サイトには [様々なフレームワークでの Example](https://mui.com/material-ui/getting-started/example-projects/) が掲載されております。
その中にはもちろん [Next.js (TypeScript) でのExample](https://github.com/mui/material-ui/tree/master/examples/nextjs-with-typescript) も掲載されています。
基本的にはそのExampleのとおりにやれば、MUIを導入できるのですが…

そのExampleが、色々とツッコミどころがあるのです。
例えば、 any 使いまくるのは TypeScript的にどうなの？

というわけで、Exampleの微妙なところを徹底的に直してみましたので、ぜひ参考にしていただければうれしいです。
ではいきましょう！

## 前提条件

ライブラリのバージョン

```
Next.js  12.2.4
MUI      5.10.1
```

## リポジトリはこちら

今回のリポジトリをこちらに公開しておきます。
ぜひ参考にしてみてください。

https://github.com/yuto-ono/next-mui-sample

## any を撲滅する

公式Exampleの [_document.tsx](https://github.com/mui/material-ui/blob/master/examples/nextjs-with-typescript/pages/_document.tsx) には、 any が結構使われています。たとえば

```tsx:_document.tsx
export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        {/* 略 */}
        {(this.props as any).emotionStyleTags}
        {/* 略 */}
      </Html>
    );
  }
}
```

`this.props as any` と書いてありますね。
これは props の中に emotionStyleTags が定義されていないから、無理やり any に変換しているわけで、
根本的に解決するなら、 emotionStyleTags を定義すればOKです。こんな感じで↓

```diff_tsx:_document.tsx
+ type MyDocumentProps = {
+   emotionStyleTags: EmotionJSX.Element[]
+ }
+ 
- export default class MyDocument extends Document {
+ export default class MyDocument extends Document<MyDocumentProps> {
    render() {
      return (
        <Html lang="en">
          {/* 略 */}
-         {(this.props as any).emotionStyleTags}
+         {this.props.emotionStyleTags}
          {/* 略 */}
        </Html>
      );
    }
  }
```

他にも any が使われている箇所があるので、型を調べて、どんどん潰していきます。
詳しくは、[修正した後のコード](https://github.com/yuto-ono/next-mui-sample/blob/main/pages/_document.tsx)をご覧ください。

## let を撲滅する

公式Exampleの [createEmotionCache.ts](https://github.com/mui/material-ui/blob/master/examples/nextjs-with-typescript/src/createEmotionCache.ts) の変数定義に let が使われています。

```createEmotionCache.ts
import createCache from '@emotion/cache';

const isBrowser = typeof document !== 'undefined';

// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
// This assures that MUI styles are loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache() {
  let insertionPoint;

  if (isBrowser) {
    const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
      'meta[name="emotion-insertion-point"]',
    );
    insertionPoint = emotionInsertionPoint ?? undefined;
  }

  return createCache({ key: 'mui-style', insertionPoint });
}
```

`let insertionPoint;` と書いてありますね。
もちろん、 let は完全な悪ではありませんが、使わずにすむのであればそうしたいものです。
（参考: [JavaScriptからletを絶滅させ、constのみにするためのレシピ集](https://qiita.com/kiyoshiro/items/13c60fad1f5279993fa2)）

というわけで、 let をなくし、 const を使えるようリファクタリングしてみました↓

```createEmotionCache.ts
import createCache from "@emotion/cache"
import type { EmotionCache } from "@emotion/cache"

// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
// This assures that MUI styles are loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
const getInsertionPoint = (): HTMLMetaElement | undefined => {
  if (typeof document !== "undefined") {
    const insertionPoint = document.querySelector<HTMLMetaElement>(
      'meta[name="emotion-insertion-point"]'
    )
    if (insertionPoint != null) {
      return insertionPoint
    }
  }
}

const createEmotionCache = (): EmotionCache => {
  return createCache({ key: "mui-style", insertionPoint: getInsertionPoint() })
}

export default createEmotionCache

```

（ちなみに、 function よりもアロー関数の方が好きなので、ついでに変えています）
修正した後のソースコードは[こちらから](https://github.com/yuto-ono/next-mui-sample/blob/main/src/mui/createEmotionCache.ts)ご覧いただけます。

## まとめ

MUI公式のExampleは、もちろんそのまま動きますし、そのまま使うのが悪いとは思いません。
とはいえ、こういう公式サイトに載っているソースコードを、思考停止でそのまま使うのではなく、

なぜそういう書き方をするのか、本当にこの書き方でいいのか

など考えられるようになると、エンジニアとして成長できるのではないかと思います。

というわけで、しっかりと思考を巡らせていきましょう！