TypeScriptでReact

# 概要

ReactをTypeScriptでやってみます。
まずは、情報収集など。

# 環境

```shell
$ yarn --version
1.15.2
$ create-react-app --version
2.1.8
```

# 三行まとめ

- React + TypeScriptについて調べた
- TypeScript有無のプロジェクトを比較した
- SFC?FC?

# 情報収集

ググって収集できた情報源。
意外とヒットしなかった印象。
まだあまり使われていない組み合わせ？

https://facebook.github.io/create-react-app/docs/adding-typescript
https://www.typescriptlang.org/docs/handbook/react-&-webpack.html
https://www.typescriptlang.org/docs/handbook/jsx.html
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
https://qiita.com/alfas/items/539ade65926deb530e0e
https://typescript-jp.gitbook.io/deep-dive/tsx/react
https://github.com/Microsoft/TypeScript-React-Starter#typescript-react-starter
https://qiita.com/namaozi/items/7446804126a055caf254
https://www.dkrk-blog.net/javascript/react_ts03

# プロジェクト作成

`create-react-app`で`--typescript`有無のプロジェクトを作成し、比較する。
`node_modules`は比較から除外。

```shell
$ create-react-app tsx-react-app # typescript無し
$ tree -I node_modules ./jsx-react-app/
./jsx-react-app/
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   └── serviceWorker.js
└── yarn.lock

2 directories, 13 files

$ create-react-app tsx-react-app --typescript # typescript有り
$ tree -I node_modules ./tsx-react-app/
./tsx-react-app/
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── react-app-env.d.ts
│   └── serviceWorker.ts
├── tsconfig.json
└── yarn.lock

2 directories, 15 files
```

# プロジェクト比較

```diff
$ diff --new-file --unified ./jsx-react-app/ ./tsx-react-app/
共通のサブディレクトリー: ./jsx-react-app/.git と ./tsx-react-app/.git
diff --new-file --unified ./jsx-react-app/README.md ./tsx-react-app/README.md
--- ./jsx-react-app/README.md	2019-03-23 20:42:45.877562453 +0900
+++ ./tsx-react-app/README.md	2019-03-23 20:41:53.565223301 +0900
@@ -42,27 +42,3 @@
 You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
 
 To learn React, check out the [React documentation](https://reactjs.org/).
-
-### Code Splitting
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
-
-### Analyzing the Bundle Size
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
-
-### Making a Progressive Web App
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
-
-### Advanced Configuration
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
-
-### Deployment
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
-
-### `npm run build` fails to minify
-
-This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
共通のサブディレクトリー: ./jsx-react-app/node_modules と ./tsx-react-app/node_modules
diff --new-file --unified ./jsx-react-app/package.json ./tsx-react-app/package.json
--- ./jsx-react-app/package.json	2019-03-23 20:42:45.877562453 +0900
+++ ./tsx-react-app/package.json	2019-03-23 20:41:53.561223273 +0900
@@ -1,11 +1,16 @@
 {
-  "name": "jsx-react-app",
+  "name": "tsx-react-app",
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@types/jest": "24.0.11",
+    "@types/node": "11.11.6",
+    "@types/react": "16.8.8",
+    "@types/react-dom": "16.8.3",
     "react": "^16.8.5",
     "react-dom": "^16.8.5",
-    "react-scripts": "2.1.8"
+    "react-scripts": "2.1.8",
+    "typescript": "3.3.4000"
   },
   "scripts": {
     "start": "react-scripts start",
共通のサブディレクトリー: ./jsx-react-app/public と ./tsx-react-app/public
共通のサブディレクトリー: ./jsx-react-app/src と ./tsx-react-app/src
diff --new-file --unified ./jsx-react-app/tsconfig.json ./tsx-react-app/tsconfig.json
--- ./jsx-react-app/tsconfig.json	1970-01-01 09:00:00.000000000 +0900
+++ ./tsx-react-app/tsconfig.json	2019-03-23 20:41:53.833225155 +0900
@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "esnext"
+    ],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "preserve"
+  },
+  "include": [
+    "src"
+  ]
+}
diff --new-file --unified ./jsx-react-app/yarn.lock ./tsx-react-app/yarn.lock
--- ./jsx-react-app/yarn.lock	2019-03-23 20:42:43.465547689 +0900
+++ ./tsx-react-app/yarn.lock	2019-03-23 20:41:52.557216320 +0900
@@ -970,16 +970,53 @@
     "@svgr/plugin-svgo" "^4.0.3"
     loader-utils "^1.1.0"
 
+"@types/jest-diff@*":
+  version "20.0.1"
+  resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"
+  integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==
+
+"@types/jest@24.0.11":
+  version "24.0.11"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.11.tgz#1f099bea332c228ea6505a88159bfa86a5858340"
+  integrity sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==
+  dependencies:
+    "@types/jest-diff" "*"
+
 "@types/node@*":
   version "11.10.5"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-11.10.5.tgz#fbaca34086bdc118011e1f05c47688d432f2d571"
   integrity sha512-DuIRlQbX4K+d5I+GMnv+UfnGh+ist0RdlvOp+JZ7ePJ6KQONCFQv/gKYSU1ZzbVdFSUCKZOltjmpFAGGv5MdYA==
 
+"@types/node@11.11.6":
+  version "11.11.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a"
+  integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==
+
+"@types/prop-types@*":
+  version "15.7.0"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.0.tgz#4c48fed958d6dcf9487195a0ef6456d5f6e0163a"
+  integrity sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg==
+
 "@types/q@^1.5.1":
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18"
   integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==
 
+"@types/react-dom@16.8.3":
+  version "16.8.3"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.3.tgz#6131b7b6158bc7ed1925a3374b88b7c00481f0cb"
+  integrity sha512-HF5hD5YR3z9Mn6kXcW1VKe4AQ04ZlZj1EdLBae61hzQ3eEWWxMgNLUbIxeZp40BnSxqY1eAYLsH9QopQcxzScA==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react@*", "@types/react@16.8.8":
+  version "16.8.8"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.8.tgz#4b60a469fd2469f7aa6eaa0f8cfbc51f6d76e662"
+  integrity sha512-xwEvyet96u7WnB96kqY0yY7qxx/pEpU51QeACkKFtrgjjXITQn0oO1iwPEraXVgh10ZFPix7gs1R4OJXF7P5sg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^2.2.0"
+
 "@types/tapable@1.0.2":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.2.tgz#e13182e1b69871a422d7863e11a4a6f5b814a4bd"
@@ -2858,6 +2895,11 @@
   dependencies:
     cssom "0.3.x"
 
+csstype@^2.2.0:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.3.tgz#b701e5968245bf9b08d54ac83d00b624e622a9fa"
+  integrity sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg==
+
 cyclist@~0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
@@ -9355,6 +9397,11 @@
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
+typescript@3.3.4000:
+  version "3.3.4000"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.4000.tgz#76b0f89cfdbf97827e1112d64f283f1151d6adf0"
+  integrity sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==
+
 uglify-js@3.4.x, uglify-js@^3.1.4:
   version "3.4.9"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
```

## プロジェクトの主な違い
- tsxでは、READMEの後半部が削除されている
- tsxでは、`package.json`と`yarn.lock`に、typescript関連が追加されている
- tsxでは、`tsconfig.json`が新規追加されている

# publicフォルダ比較

```shell
$ diff --report-identical-files ./jsx-react-app/public/ ./tsx-react-app/public/
ファイル ./jsx-react-app/public/favicon.ico と ./tsx-react-app/public/favicon.ico は同一です
ファイル ./jsx-react-app/public/index.html と ./tsx-react-app/public/index.html は同一です
ファイル ./jsx-react-app/public/manifest.json と ./tsx-react-app/public/manifest.json は同一です
```

# srcフォルダ比較

```shell
$ diff --brief --report-identical-files ./jsx-react-app/src/ ./tsx-react-app/src/
ファイル ./jsx-react-app/src/App.css と ./tsx-react-app/src/App.css は同一です
./jsx-react-app/src/ のみに存在: App.js
./jsx-react-app/src/ のみに存在: App.test.js
./tsx-react-app/src/ のみに存在: App.test.tsx
./tsx-react-app/src/ のみに存在: App.tsx
ファイル ./jsx-react-app/src/index.css と ./tsx-react-app/src/index.css は異なります
./jsx-react-app/src/ のみに存在: index.js
./tsx-react-app/src/ のみに存在: index.tsx
ファイル ./jsx-react-app/src/logo.svg と ./tsx-react-app/src/logo.svg は同一です
./tsx-react-app/src/ のみに存在: react-app-env.d.ts
./jsx-react-app/src/ のみに存在: serviceWorker.js
./tsx-react-app/src/ のみに存在: serviceWorker.ts
```

# .jsファイルと.tsxファイル比較
```diff:App.jsとApp.tsx
$ diff --unified --report-identical-files ./jsx-react-app/src/App.js ./tsx-react-app/src/App.tsx
--- ./jsx-react-app/src/App.js	2019-03-23 20:42:45.877562453 +0900
+++ ./tsx-react-app/src/App.tsx	2019-03-23 20:41:53.565223301 +0900
@@ -9,7 +9,7 @@
         <header className="App-header">
           <img src={logo} className="App-logo" alt="logo" />
           <p>
-            Edit <code>src/App.js</code> and save to reload.
+            Edit <code>src/App.tsx</code> and save to reload.
           </p>
           <a
             className="App-link"
```

```shell:App.test.jsとApp.test.tsx
$ diff --unified --report-identical-files ./jsx-react-app/src/App.test.js ./tsx-react-app/src/App.test.tsx
ファイル ./jsx-react-app/src/App.test.js と ./tsx-react-app/src/App.test.tsx は同一です
```

```shell:index.jsとindex.tsx
$ diff --unified --report-identical-files ./jsx-react-app/src/index.js ./tsx-react-app/src/index.tsx
ファイル ./jsx-react-app/src/index.js と ./tsx-react-app/src/index.tsx は同一です
```

```diff:serviceWorker.jsとserviceWorker.ts
$ diff --unified --report-identical-files ./jsx-react-app/src/serviceWorker.js ./tsx-react-app/src/serviceWorker.ts
--- ./jsx-react-app/src/serviceWorker.js	2019-03-23 20:42:45.877562453 +0900
+++ ./tsx-react-app/src/serviceWorker.ts	2019-03-23 20:41:53.565223301 +0900
@@ -20,10 +20,18 @@
     )
 );
 
-export function register(config) {
+type Config = {
+  onSuccess?: (registration: ServiceWorkerRegistration) => void;
+  onUpdate?: (registration: ServiceWorkerRegistration) => void;
+};
+
+export function register(config?: Config) {
   if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
     // The URL constructor is available in all browsers that support SW.
-    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
+    const publicUrl = new URL(
+      (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
+      window.location.href
+    );
     if (publicUrl.origin !== window.location.origin) {
       // Our service worker won't work if PUBLIC_URL is on a different origin
       // from what our page is served on. This might happen if a CDN is used to
@@ -54,7 +62,7 @@
   }
 }
 
-function registerValidSW(swUrl, config) {
+function registerValidSW(swUrl: string, config?: Config) {
   navigator.serviceWorker
     .register(swUrl)
     .then(registration => {
@@ -98,7 +106,7 @@
     });
 }
 
-function checkValidServiceWorker(swUrl, config) {
+function checkValidServiceWorker(swUrl: string, config?: Config) {
   // Check if the service worker can be found. If it can't reload the page.
   fetch(swUrl)
     .then(response => {
```

また、`react-app-env.d.ts`はtsxのみ

```typescript:react-app-env.d.ts
/// <reference types="react-scripts" />

```

# 感想

- React + TypeScriptは、まだまだこれから？
- Stateless Functional Component(SFC)はなくなった？
- Function Components(FC)ができた？
- 何か作って理解を深めよう
- TypeScriptを使いたいならAngular？
- `tsconfig.json`とは？
- `react-app-env.d.ts`とは？

以上