Slide 1

Slide 1 text

LINE NEWS を TypeScript 化したい!

Slide 2

Slide 2 text

井上明紀 フロントエンドエンジニア LINE 株式会社 UIT User Interface + Technologu team @LINE corp 2019年新卒入社 趣味

Slide 3

Slide 3 text

これまでに係わったプロダクト LINE NEWS LINE スケジュール

Slide 4

Slide 4 text

LINE NEWS とは ここ 月間アクティブユーザー(MAU) 6,800万 ユーザー 月間ページビュー(MPV) 120億 PV ※2019年7月末時点

Slide 5

Slide 5 text

JavaScript + React.js エンジニア 20人超 の 大規模開発プロダクト ネイティブに見えるけど 全部 Web LINE NEWS とは

Slide 6

Slide 6 text

なんで TypeScript 化したいの?

Slide 7

Slide 7 text

LINE NEWS の開発 Planned release 開発のひとたち A B 1% 99% 開発人数もコード量も多い 2週に1回の頻度の高いリリース ABテストや機能の追加・削除が頻繁に行われる

Slide 8

Slide 8 text

LINE NEWS の開発 Planned release 開発人数もコード量も多い 2週に1回の頻度の高いリリース ABテストや機能の追加・削除が頻繁に行われる A B 1% 99% 開発のひとたち キャッチアップが困難!

Slide 9

Slide 9 text

LINE NEWS の品質保証 影響範囲 LINE NEWSのフロントエンド開発で良く耳にする言葉 コードの変更 QA確認 不具合修正 QA確認 不具合があれば 報告 QA: Quality Assurance

Slide 10

Slide 10 text

ちょっとしたミスがあった場合 修正後QAコストかかる ちょっとしたミスの例 開発スピード遅い + QAリソースを使ってしまう 型があったら実行しなくても発見できるミス

Slide 11

Slide 11 text

チームの声 変数のデータ構造が分からない! このpropsのitemはどういう データを持つの?nullable?

Slide 12

Slide 12 text

変数のデータ構造が分からない! この props の item はどういう データを持つの? nullable? 機能改修が安全に行えているのか 自信がない 影響範囲大丈夫かな ... チームの声

Slide 13

Slide 13 text

変数のデータ構造が分からない! このpropsのitemはどういう データを持つの?nullable? 機能改修が安全に行えているのか 自信がない 影響範囲大丈夫かな... 他の言語で慣れ親しんできた 言語機能がJavaScriptにない Interface 使いたい...! チームの声

Slide 14

Slide 14 text

変数のデータ構造が分からない! このpropsのitemはどういう データを持つの?nullable? 機能改修が安全に行えているのか 自信がない 影響範囲大丈夫かな... 他の言語で慣れ親しんできた 言語機能がJavaScriptにない Interface 使いたい...! TypeScript 化しよう...! チームの声

Slide 15

Slide 15 text

やったこと

Slide 16

Slide 16 text

開発環境のモダン化 チームやプロダクトの大規模化に合わせて、環境全体のモダン化 TypeScript導入 Reactバージョンアップ Test(ユニットテスト、自動テスト) コードフォーマッティング整備

Slide 17

Slide 17 text

% npm run eject など create-react-app の eject TS化にあたって Create React App 依存から脱却 create-react-appに隠蔽されていたファイルが吐き出される webpack package.json Build Script Babel

Slide 18

Slide 18 text

Eject した後のファイルを見直そう! 設定ファイルの見直し ● 不要な設定の見直し ● 古くなっていたパッケージの更新 ○ webpack 3系 → 4系 ○ Babel 6系 → 7系 コードの見直し 環境変数として process.REACT_ENV を使用していた箇所の修正

Slide 19

Slide 19 text

webpack.config.js tsconfig.json .babelrc eslintrc jest.config.js この5つのファイルを編集します!

Slide 20

Slide 20 text

● Development と Production の2つの設定ファイル ● 細かい変更 ○ dst のディレクトリやファイルの指定 ○ webpack-dev-server の設定 ○ DefinePlugin による NODE_ENV の設定 webpack.cofig.js を書こう! webpack.config.js

Slide 21

Slide 21 text

TypeScriptを扱うときの選択肢 es5 es5 + polyfill このどっちかでいける webpack.config.js webpack.cofig.js を書こう! tsc webpack + ts-loader webpack + ts-loader + babel-loader webpack + babel-loader + @babel/preset-typescript

Slide 22

Slide 22 text

tsc webpack + ts-loader TypeScriptを扱うときの選択肢 es5 es5 + polyfill webpack + ts-loader + babel-loader webpack + babel-loader + @babel/preset-typescript これを選択 webpack.config.js webpack.cofig.js を書こう!

Slide 23

Slide 23 text

target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tscofig.json を書こう! compilerOptions tsconfig.json

Slide 24

Slide 24 text

compilerOptions target target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tsconfig.json tscofig.json を書こう!

Slide 25

Slide 25 text

compilerOptions lib target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) 実行時の環境でサポートされていないと実行できない tsconfig.json tscofig.json を書こう!

Slide 26

Slide 26 text

https://github.com/microsoft/TypeScript/tree/master/lib esnext > es2020 > es2020.promise ※使う場合はpolyfillが必要!(ex. core-js/modules/es.promise.all-settled) compilerOptions lib tsconfig.json tscofig.json を書こう!

Slide 27

Slide 27 text

compilerOptions module target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tsconfig.json tscofig.json を書こう!

Slide 28

Slide 28 text

babel-loader (@babel/preset-env)設定 compilerOptions module tsconfig.json tscofig.json を書こう!

Slide 29

Slide 29 text

compilerOptions allowJs target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tsconfig.json tscofig.json を書こう!

Slide 30

Slide 30 text

jsファイルのある程度の型を推論 ● 戻り値 ● オブジェクト ● 引数(引数を持たない場合のみ) 逆に一時的にundefinedを返している関数 等は.d.tsで正しく定義する必要あり jsをインポート sample.js index.ts tsconfig.json compilerOptions allowJs tscofig.json を書こう!

Slide 31

Slide 31 text

allowJsがtrueの場合にnoImplicitAnyの効力が薄まる any として扱われる React Componentのpropsもなんでもありに... tsconfig.json compilerOptions allowJs tscofig.json を書こう!

Slide 32

Slide 32 text

tsconfig.json compilerOptions jsx target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tscofig.json を書こう!

Slide 33

Slide 33 text

tsconfig.json compilerOptions allowSyntheticDefaultImports target es5 出力するjsのesバージョン lib esnext, dom, dom.iterable 利用できる標準ライブラリの型 module es2015 出力するjsのモジュール管理方法 allowJs true JSの読み込みの許可と型の推論 jsx react 出力するjsのJSX記法への扱い allowSynthetic DefaultImports true export assignment を export defaultとして解釈 (ex. export = React;) tscofig.json を書こう!

Slide 34

Slide 34 text

@babel/preset-env { "presets": [ ["@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3 }] ] } .babelrc 使ったプリセットは1つ .babelrc を書こう!

Slide 35

Slide 35 text

.babelrc を書こう! const array = [1, 2, 3]; array.forEach(x => console.log(x)); import "core-js/modules/es.array.for-each"; var array = [1, 2, 3]; array.forEach(function (x) { return console.log(x); }); 必要に応じて core-js/modules 下をインポート 試しに @babel/cli でビルドしてみると ... babel-loader の場合は webpack が import を 解決するので不安であれば手動も可! .babelrc useBuiltIns: “usage” の挙動

Slide 36

Slide 36 text

● js と ts で CI で落とす基準を変更 ● js だけ deprecated な機能など warning が既に多数出ていたため許容 ● ts では warning を許容せず厳しく適用 今までの LINE NEWS では ● eslintrc はあったが warning は許容されていた .eslintrc .eslintrc.js を書こう!

Slide 37

Slide 37 text

extendsプロパティ .eslintrc.js を書こう! .eslintrc ● eslint:recommended ● plugin:react/recommended ● plugin:prettier/recommended ● prettier/react ● plugin:@typescript-eslint/recommended ● plugin:@typescript-eslint/eslint-recommended ● prettier/@typescript-eslint

Slide 38

Slide 38 text

extendsプロパティ JavaScript .eslintrc .eslintrc.js を書こう! ● eslint:recommended ● plugin:react/recommended ● plugin:prettier/recommended ● prettier/react ● plugin:@typescript-eslint/recommended ● plugin:@typescript-eslint/eslint-recommended ● prettier/@typescript-eslint

Slide 39

Slide 39 text

extendsプロパティ JavaScript TypeScript .eslintrc .eslintrc.js を書こう! ● eslint:recommended ● plugin:react/recommended ● plugin:prettier/recommended ● prettier/react ● plugin:@typescript-eslint/recommended ● plugin:@typescript-eslint/eslint-recommended ● prettier/@typescript-eslint

Slide 40

Slide 40 text

extendsプロパティ JavaScript ● eslint:recommended ● plugin:react/recommended ● plugin:prettier/recommended ● prettier/react ● plugin:@typescript-eslint/recommended ● plugin:@typescript-eslint/eslint-recommended ● prettier/@typescript-eslint TypeScript .eslintrc .eslintrc.js を書こう!

Slide 41

Slide 41 text

rules: { 'react/jsx-uses-vars': 'warn', 'react/prop-types': 'off', 'react/no-string-refs': 'warn', // string refs is deprecated 'react/no-deprecated': 'off', // deprecated methods 'react/display-name': 'warn', // Component's display name is necessary 'react/no-unescaped-entities': 'warn', // should be error 'react/jsx-key': 'warn', // should be error 'no-unused-vars': 'warn', // should be error 'no-redeclare': 'warn', // should be error 'no-async-promise-executor': 'warn', // should be error 'react/jsx-no-duplicate-props': 'warn', // should be error 'no-case-declarations': 'warn', 'no-prototype-builtins': 'warn', }, Prettierで全てfix! それでもエラーが発生する場合はwarnで許容 .eslintrc.js を書こう! .eslintrc

Slide 42

Slide 42 text

jest.config.js を書こう! module.exports = { preset: "ts-jest", transform: { '^.+¥¥.(ts|js|tsx)$': 'ts-jest', }, setupFilesAfterEnv: ['./test/setupTests.ts'], }; jest.config.js

Slide 43

Slide 43 text

module.exports = { preset: "ts-jest", transform: { '^.+¥¥.(ts|js|tsx)$': 'ts-jest', }, setupFilesAfterEnv: ['./test/setupTests.ts'], }; jest.config.js jest.config.js を書こう! tsのテストのために ts-jestを利用

Slide 44

Slide 44 text

module.exports = { preset: "ts-jest", transform: { '^.+¥¥.(ts|js|tsx)$': 'ts-jest', }, setupFilesAfterEnv: ['./test/setupTests.ts'], }; jest.config.js tsのテストのために ts-jestを利用 reactのユーティリティ (Enzyme)の設定など jest.config.js を書こう!

Slide 45

Slide 45 text

LINE NEWSのTS化後の開発

Slide 46

Slide 46 text

TS化に伴い発生した問題 TS化に起因するバグ

Slide 47

Slide 47 text

TS化に起因するバグ なし TS化に伴い発生した問題

Slide 48

Slide 48 text

● eslintのfixの不具合で問題があった ● node-sass等のバージョンアップでsassのビルドが失敗していた TS化に起因するバグ なし しいて言えば TS化に伴い発生した問題

Slide 49

Slide 49 text

● JSファイルは無理にTSに移行しない ● 新規ファイルは必ずTSファイル ● JSの変更箇所は影響範囲を考慮してTS移行 ○ A.js → A.tsにフルで移行 ○ A.js + A.tsでモジュール単位で移行 ○ A.js + A.d.ts .js → .ts のポリシー 影響範囲は新規や改修時のQA範囲と同じ or 少し増える程度

Slide 50

Slide 50 text

まとめ

Slide 51

Slide 51 text

webpack.config.js tsconfig.json .babelrc eslintrc jest.config.js create-react-app を eject % npm run eject 5つのコンフィグを作成 .js → .ts の移行ポリシーを作成 ● JS ファイルは無理に TS に移行しない ● 新規ファイルは必ず TS ファイル ● JS の変更箇所は影響範囲を考慮して TS 移行

Slide 52

Slide 52 text

ありがとうございました