TypescriptにSCSSをimportした上に型定義を使う

# はじめに
Reactを使う時、css-loaderで`:local(.className)`または`:local{...}`と宣言した場合にclassNameがハッシュ化されexportされる。
それらのモジュールをimportする時、vscode(またはその他IDE)の[IntelliSense](https://code.visualstudio.com/docs/editor/intellisense)を使えると大変便利だと思う。
その方法をメモします。

# やり方
## フォルダ構成
```

+- demo-app/
   +- package.json
   +- tsconfig.json
   +- webpack.config.js
   +- html/
     +- index.html
   +- scripts/
     +- foo.tsx
   +- styles/
     +- foo.scss
     +- foo.scss.d.ts
```

## css-loaderの導入
割愛します。
一応`webpack.config.js`の`extensions`の領域に下記の拡張子を追加するのを忘れずに。

```webpack.config.js
resolve: {
  extensions: [... ".sass", ".scss", ".css"]
}
```

## スタイルの記入
```styles/foo.scss
// ここはGlobaｌ Style
* {
  color: royalblue;
}

// ここはScroped Style
:local {
  .bar {
    background-color: aquamarine;
  }
  .baz {
    font-size: 2em;
  }
}
```

## スタイルの仮導入
`.d.ts`を作成しなくても、ｔｓファイルの中で下記の記述のように`require()`を使えば、別に導入できなくもないですが、TSスタイルではないので、ちゃんと`import`できるようにする。

```scripts/foo.tsx
const style = require('styles/foo')
```

## 型定義ファイル`.d.ts`の作成
TSにJSファイルを導入する時と同じように、`ファイル名.d.ts`を作成すればOK。
記述の仕方は[こちら](https://stackoverflow.com/questions/40382842/cant-import-css-scss-modules-typescript-says-cannot-find-module)を参考していた。

```styles/foo.scss.d.ts
declare module '*.scss' {
  const content: {
    bar: string;
    baz: string;
  };
  export default content;
}
```

## TSにimport

```scripts/foo.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'

import styles from 'styles/foo.scss' // 拡張子省略不可

const Hello: React.FC<{}> = props => (
  <div className={styles.bar}>Hello World!
  </div>
)

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello />,
    document.body.appendChild(document.createElement('div')),
  )
})
```

問題なければ、変数`styles`を呼ぶ時、定義したclassはIntelliSenseに表示される。
![hospital_infos_tsx_—_byouten-junior.png](https://qiita-image-store.s3.amazonaws.com/0/87438/5b7754df-93b7-20e7-60d1-9b0495421db3.png)

## 結果
結果はこんな感じになる

![ByoutenJunior.png](https://qiita-image-store.s3.amazonaws.com/0/87438/4d13e0cb-111c-40b5-f8e0-6d146067a3e7.png)
![ByoutenJunior.png](https://qiita-image-store.s3.amazonaws.com/0/87438/f7f5b863-ac28-fda4-aa16-b78b95775aa6.png)
![ByoutenJunior.png](https://qiita-image-store.s3.amazonaws.com/0/87438/c4a674fb-a2c7-63ea-9008-d59e1643ba99.png)

ここのclass名はコンパイルする度に変わる。

## 注意点
上記`foo.scss.d.ts`の書き方だと、下記の構文でのみ読み込める

```ts
import styles from 'styles/foo.scss'
```

以下の`export default content`を修正すれば、別の書き方でimportできる

```diff
declare module '*.scss' {
  const content: {
    bar: string;
    baz: string;
  };
- export default content;
+ export = content;
}
```

```ts
import * as styles from 'styles/foo.scss'
// または
import {foo, bar} from 'styles/foo.scss'
```

## ちょっとスマートな使い方
前述のやり方だと、1つ`.scss`ファイルに1つ`.d.ts`ファイルを作らなきゃいけなくなるので、スタイルファイルが多い場合は大変見づらい。
`styles/`配下に`index.d.ts`を作って、先ほど`styles/foo.scss`の内容を移動すればいいと思う。

```styles/index.d.ts
declare module 'styles/foo.scss' {
  const content: {
    bar: string;
    baz: string;
  };
  export = content;
}

declare module 'styles/bar.scss' {
  const content: {
    baz: string;
  };
  export = content;
}
```

こう記述すると、より綺麗にまとまると思う

## 余談
railsのwebpackerを使う時、デフォルトはjs/tsファイルのみwatchするが、下記の記述を`config/application.rb`に追加すれば、cssファイル(とscssファイル)もwatchしてくれる。

```config/application.rb
Webpacker::Compiler.watched_paths += %w(PATH/TO/STYLES/**/*.css PATH/TO/STYLES/**/*.scss)
```

詳しい使い方は[ここ](https://github.com/rails/webpacker#watched)を参照してください。

# 参考
https://qiita.com/nullabletypo/items/d3ac5f15f2405f9ac3fd
https://stackoverflow.com/questions/40382842/cant-import-css-scss-modules-typescript-says-cannot-find-module
https://qiita.com/terrierscript/items/56d2cc15f76df50dfee7