styled-componentsのcss propを、create-react-appで作成したReact & TypeScriptの環境で動かすためのチュートリアル

styled-componentsにはv4からcss propという機能があり、これを用いるとちょっとしたスタイルの修正を、コンポーネントの作成無しで実施できる。

しかしこのcss prop, どうも一筋縄では動かないらしい。特にTypeScript環境では以下のようにハマる人も多いようだ。

https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245
https://github.com/styled-components/styled-components/issues/2528

私もこの点で大ハマりし半日を潰す羽目になった。
ネット上の記事には断片的な情報しか無く、同じハマり方をする人もいると思うので、備忘録も兼ねて記録を残す。

私の知識が足りないだけなのだと思うが、この件ではネットの記事を見ながら「せめて何処に書くか位ちゃんと書けや！！！」とか「ここまで書いてあってなんであの情報ないんだよ！(バージョン差分の影響かもしれない)」と感じたことが多々あったので、本記事ではチュートリアル形式で、可能な限り再現しやすい形をとる。

具体的にはcreate-react-appでReactアプリを作成するところから、styled-componentsをインストールし、css propの機能によりスタイルを適用するまでの手順を示す。同じ悩みにぶち当たった人の助けになれば。

# 前提
本チュートリアルは以下の環境を前提とする
- react: 16.13.0
- create-react-app: 3.3.0
- styled-components: 5.0.1

おそらくこの手順は "create-react-app"を用いた環境でのみ有効。

# css prop動作確認手順
## create-react-app
`--typescript`オプションを使用する

```bash
$ create-react-app trycssprop --typescript
```

## styled-componentのインストール

```bash
$ yarn add styled-components
```

型定義ファイルも同時にインストールする

```bash
$ yarn add @types/styled-components
```

## yarn start でアプリが正常にビルドされることを確認
```bash
$ yarn start
```

<img width="1332" alt="スクリーンショット 2020-03-09 19.54.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/c118452e-1204-c9c5-b384-03c670d6d283.png">

## styled-componentの基本動作確認
スタイルを付加したコンポーネントを作成する。

```diff
diff --git a/src/App.tsx b/src/App.tsx
index a53698a..df0b1c0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,11 @@
 import React from 'react';
 import logo from './logo.svg';
 import './App.css';
+import styled from 'styled-components'
+
+const Test = styled.div`
+  color: red;
+`
 
 function App() {
   return (
@@ -18,6 +23,7 @@ function App() {
         >
           Learn React
         </a>
+        <Test> TEST!!!!!! </Test>
       </header>
     </div>
   );
```
<img width="1320" alt="スクリーンショット 2020-03-09 20.00.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/732fb800-9c22-130f-5494-6983a7fc6c2f.png">



## ここからが本題！ css propを用いてスタイルを当てる
まず何もせずにcss propを使ってみる。任意のコンポーネントに`css="**some style**"`の形でpropを設定することで、スタイルを適用できる。
https://styled-components.com/docs/api#css-prop

```diff
diff --git a/src/App.tsx b/src/App.tsx
index df0b1c0..f057b5c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -23,7 +23,7 @@ function App() {
         >
           Learn React
         </a>
-        <Test> TEST!!!!!! </Test>
+        <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>
       </header>
     </div>
   );
```

ビルドすると以下のようなエラーが表示される。
これは`css="**some style**"`がJSXの拡張であり、現状の設定ではBabelが読み取れないことに起因する(らしい)。

```bash
Failed to compile.

trycssprop/src/App.tsx
TypeScript error in trycssprop/src/App.tsx(26,15):
No overload matches this call.
  Overload 1 of 2, '(props: Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | "title" | ... 252 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
    Type '{ children: string; css: string; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
      Property 'css' does not exist on type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<"div", any, {}, never>): ReactElement<StyledComponentPropsWithAs<"div", any, {}, never>, string | ... 1 more ... | (new (props: any) => Component<...>)>', gave the following error.
    Type '{ children: string; css: string; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
      Property 'css' does not exist on type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.  TS2769

    24 |           Learn React
    25 |         </a>
  > 26 |         <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>
       |               ^
    27 |       </header>
    28 |     </div>
    29 |   );
```

## 型定義ファイルへのimportの追加
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245
の記載を参照して、

`import {} from 'styled-components/cssprop'`を型定義ファイルに追記する。

なぜ型定義ファイルに上記のimportを追記するのか？ これに関しては大変申し訳ないのだが、私自身理解していない。
試行錯誤の結果、型定義ファイルに記載すれば良さそうということだけ分かった。

この辺りが気になる方は、「styled-components css props typescript」あたりでググってみてほしい。
いくつかの記事から、この推論にたどり着くことがなんとなく理解いただけると思う。
もしくはこの理由をご存知の方は、是非ともコメントをいただきたい。

当初'node_modules/@types/react/index.d.ts'に追記していたが、
gitで管理する必要等も考えると 独自に型定義ファイルを作成するのが良さそうだ。

私は以下を参考に作成した。
https://qiita.com/mtgto/items/e30d1529ca298e49557e

具体的には、
- types/cssprop/index.d.tsを作成
- 上述の`import {} from 'styled-components/cssprop'`を追記
- tsconfigに独自作成の型定義ファイルの場所を指定
を実施した。具体的なコードは以下の通り。

これでひとまずビルドは通るようになる。

```diff
diff --git a/src/types/cssprop/index.d.ts b/src/types/cssprop/index.d.ts
new file mode 100644
index 0000000..02edd53
--- /dev/null
+++ b/src/types/cssprop/index.d.ts
@@ -0,0 +1 @@
+import {} from 'styled-components/cssprop'
diff --git a/tsconfig.json b/tsconfig.json
index f2850b7..e335802 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,7 +17,12 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
-    "jsx": "react"
+    "jsx": "react",
+    "baseUrl": "./",
+    "typeRoots": [
+      "types",
+      "node_modules/@types"
+    ]
   },
   "include": [
     "src"

```

<img width="1320" alt="スクリーンショット 2020-03-09 20.00.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/c2b3697b-4c95-78e4-0f35-06ba0a6eca3a.png">

## 利用する'styled'関数の変更
ひとまずbuildは通るようになった。が、"border-bottom"が適用されていない。つまりcss propで設定したスタイルはまだ適用されていない。

```tsx
        <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>
```

ここで再度公式ドキュメントに戻ると以下の記載が確認できる。

https://styled-components.com/docs/api#css-prop

> You can use the Babel macro to make this work in create-react-app. Unfortunately, Babel macros only run when imported so the import can not be added automatically. The above code works perfectly if you add the import to the macro manually:

```javascript
// 公式ドキュメントより抜粋
import styled from 'styled-components/macro'

<div
  css={`
    background: papayawhip;
    color: ${props => props.theme.colors.text};
  `}
/>
<Button
  css="padding: 0.5em 1em;"
/>
```

どうも create-react-app環境では、Babel macroを利用することが出来、その際は`import styled from 'styled-components/macro'`を用いる必要があるようだ。この記載にしたがってimportを修正する。

```diff
diff --git a/src/App.tsx b/src/App.tsx
index f057b5c..a813609 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import logo from './logo.svg';
 import './App.css';
-import styled from 'styled-components'
+import styled from 'styled-components/macro'
 
 const Test = styled.div`
   color: red;
```

これで期待通りにcss propによるスタイルが適用されるようになった。
<img width="1323" alt="スクリーンショット 2020-03-09 19.53.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/f3dab4b6-baef-46ac-188a-63fcd1910bbc.png">

# さいごに
この件は半日で解消はしたものの、不要に苦しんだという印象が強い。本記事で同じ苦しみを味わう人が少しでも減ればうれしい。