LINE NEWS を TypeScript 化したい!/ How we did refactoring LINE NEWS codes into TypeScript

LINE NEWS を TypeScript 化したい!/ How we did refactoring LINE NEWS codes into TypeScript

Akinori Inoue (LINE Corporation)
UIT meetup vol.8 online「We Are TypeScripters!」での発表資料です。
https://uit.connpass.com/event/161964/
https://cluster.mu/e/2623c783-862e-4004-a058-8d1c97b040cc

53850955f15249a1a9dc49df6113e400?s=128

LINE Developers

March 18, 2020
Tweet

Transcript

  1. LINE NEWS を TypeScript 化したい!

  2. 井上明紀 フロントエンドエンジニア LINE 株式会社 UIT User Interface + Technologu team

    @LINE corp 2019年新卒入社 趣味
  3. これまでに係わったプロダクト LINE NEWS LINE スケジュール

  4. LINE NEWS とは ここ 月間アクティブユーザー(MAU) 6,800万 ユーザー 月間ページビュー(MPV) 120億 PV

    ※2019年7月末時点
  5. JavaScript + React.js エンジニア 20人超 の 大規模開発プロダクト ネイティブに見えるけど 全部 Web

    LINE NEWS とは
  6. なんで TypeScript 化したいの?

  7. LINE NEWS の開発 Planned release 開発のひとたち A B 1% 99%

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

    1% 99% 開発のひとたち キャッチアップが困難!
  9. LINE NEWS の品質保証 影響範囲 LINE NEWSのフロントエンド開発で良く耳にする言葉 コードの変更 QA確認 不具合修正 QA確認

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

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

  12. 変数のデータ構造が分からない! この props の item はどういう データを持つの? nullable? 機能改修が安全に行えているのか 自信がない

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

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

    TypeScript 化しよう...! チームの声
  15. やったこと

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

  17. % npm run eject など create-react-app の eject TS化にあたって Create

    React App 依存から脱却 create-react-appに隠蔽されていたファイルが吐き出される webpack package.json Build Script Babel
  18. Eject した後のファイルを見直そう! 設定ファイルの見直し • 不要な設定の見直し • 古くなっていたパッケージの更新 ◦ webpack 3系

    → 4系 ◦ Babel 6系 → 7系 コードの見直し 環境変数として process.REACT_ENV を使用していた箇所の修正
  19. webpack.config.js tsconfig.json .babelrc eslintrc jest.config.js この5つのファイルを編集します!

  20. • Development と Production の2つの設定ファイル • 細かい変更 ◦ dst のディレクトリやファイルの指定

    ◦ webpack-dev-server の設定 ◦ DefinePlugin による NODE_ENV の設定 webpack.cofig.js を書こう! webpack.config.js
  21. 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
  22. 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 を書こう!
  23. 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
  24. 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 を書こう!
  25. 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 を書こう!
  26. 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 を書こう!
  27. 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 を書こう!
  28. babel-loader (@babel/preset-env)設定 compilerOptions module tsconfig.json tscofig.json を書こう!

  29. 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 を書こう!
  30. jsファイルのある程度の型を推論 • 戻り値 • オブジェクト • 引数(引数を持たない場合のみ) 逆に一時的にundefinedを返している関数 等は.d.tsで正しく定義する必要あり jsをインポート

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

  32. 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 を書こう!
  33. 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 を書こう!
  34. @babel/preset-env { "presets": [ ["@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3

    }] ] } .babelrc 使ったプリセットは1つ .babelrc を書こう!
  35. .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” の挙動
  36. • js と ts で CI で落とす基準を変更 • js だけ

    deprecated な機能など warning が既に多数出ていたため許容 • ts では warning を許容せず厳しく適用 今までの LINE NEWS では • eslintrc はあったが warning は許容されていた .eslintrc .eslintrc.js を書こう!
  37. 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
  38. 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
  39. 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
  40. 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 を書こう!
  41. 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
  42. jest.config.js を書こう! module.exports = { preset: "ts-jest", transform: { '^.+¥¥.(ts|js|tsx)$':

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

    setupFilesAfterEnv: ['./test/setupTests.ts'], }; jest.config.js jest.config.js を書こう! tsのテストのために ts-jestを利用
  44. 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 を書こう!
  45. LINE NEWSのTS化後の開発

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

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

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

  49. • JSファイルは無理にTSに移行しない • 新規ファイルは必ずTSファイル • JSの変更箇所は影響範囲を考慮してTS移行 ◦ A.js → A.tsにフルで移行

    ◦ A.js + A.tsでモジュール単位で移行 ◦ A.js + A.d.ts .js → .ts のポリシー 影響範囲は新規や改修時のQA範囲と同じ or 少し増える程度
  50. まとめ

  51. 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 移行
  52. ありがとうございました