Slide 1

Slide 1 text

TypeScript へ型安全性を高めな がらリプレースする @_kimuson 1 きむそん @_kimuson

Slide 2

Slide 2 text

自己紹介 きむそん (kimsuon) Twitter: @_kimuson Github: d-kimuson Web エンジニア1年生 ブログ: kimuson.dev @_kimuson 2

Slide 3

Slide 3 text

所属 株式会社モバイルファクトリー 駅奪取チーム @_kimuson 3

Slide 4

Slide 4 text

今日の話題 TypeScript @_kimuson 4

Slide 5

Slide 5 text

TypeScript JavaScript の拡張言語 漸進的型付けによる型チェック トランスパイル 静的型によってスケールしやすい @_kimuson 5

Slide 6

Slide 6 text

今日話すこと TypeScript 移行のつらさ TypeScript 移行のプラクティス がんばらない TypeScript メリハリのある TypeScript 弊社でのリプレース効果 @_kimuson 6

Slide 7

Slide 7 text

オプション緩くするより、対象を絞れ! @_kimuson 7

Slide 8

Slide 8 text

移行するアプリケーション 駅奪取: 先日10周年を迎えた位置ゲームアプリ バックエンド: Perl フロントエンド: JavaScript + Vue.js クライアント: WebView @_kimuson 8

Slide 9

Slide 9 text

TypeScript 移行の壁 トランスパイルによる問題はほとんどない 問題は型チェック 大量の型エラーと向きあうことに @_kimuson 9

Slide 10

Slide 10 text

型アノテーションが存在しない @_kimuson 10 2 +function getUser(id: string): User { 1 -function getUser(id) { 3 // ... 4 }

Slide 11

Slide 11 text

null チェックされてない @_kimuson 11 6 +elm.style.color = 'red'; // 型エラーなし 1 const elm = document.getElementById('example'); // 値: null, 型: HTMLElement 2 +if (elm === null) { 3 + throw new Error('elm が null で想定してないよ :cry:'); 4 +} 5 -elm.style.color = 'red'; // Object is possibly 'null'.

Slide 12

Slide 12 text

これらの型エラーを潰さないと移行できない @_kimuson 12

Slide 13

Slide 13 text

全部直す? @_kimuson 13

Slide 14

Slide 14 text

安全に直せるのか? 型アノテーション ランタイムに影響がないので、安全 それ以外 本番での挙動が変わることになる 問題の切り分けが難しくなる @_kimuson 14

Slide 15

Slide 15 text

工数も厳しい 全ての関数の引数に型アノテーションを追加 全ての型エラーをきれいに修正 移行を進めている間にも新しい実装が追加されていく @_kimuson 15

Slide 16

Slide 16 text

じゃあどうするの? @_kimuson 16

Slide 17

Slide 17 text

がんばらない TypeScript @_kimuson 17

Slide 18

Slide 18 text

がんばらない TypeScript 「型チェックを緩くする」オプションが柔軟に提供 「既存コードに合わせてオプションがんがんゆるくしち ゃおうぜ」って方針 まずは導入できることが大事 @_kimuson 18

Slide 19

Slide 19 text

こんな感じでオプションを柔軟に 制御できる 1. 公式の TSConfig リファレンス ↩︎ @_kimuson 19 [1] 1 // tsconfig.json 2 { 3 "compilerOptions": { 4 "module": "commonjs", 5 "target": "es2020", 6 "outDir": "dist", 7 "strict": true, 8 "declaration": true, 9 "moduleResolution": "node", 10 "noEmit": false, 11 "skipLibCheck": true, 12 "noUncheckedIndexedAccess": true, 13 "noErrorTruncation": true, 14 "forceConsistentCasingInFileNames": true, 15 "allowJs": false 16 } 17 }

Slide 20

Slide 20 text

設定が多くてわけわからない @_kimuson 20

Slide 21

Slide 21 text

strict だけ外せば大部分のエラーは消える noImplictAny: 必要な型アノテーションが抜けててもOK strictNullCheckes: null なしでOK @_kimuson 21 strict: 厳格にするオプションをまとめたもの 1 function getUser(id) {} // id が any に解決されることで許容される 1 const elm = document.getElementById('example'); // HTMLElement 2 elm.style.color = 'red'; // strictNullCheckes で許容される

Slide 22

Slide 22 text

がんばらない TypeScript もつらいよ すべてのファイルで型が信用できなくなってしまう 後からの型安全性を高めることが難しい @_kimuson 22

Slide 23

Slide 23 text

型が信用できない…? @_kimuson 23

Slide 24

Slide 24 text

型と値がずれる number22 には '202' 文字列が入るが、型は number @_kimuson 24 1 function plus2(value /* :any */): number { 2 return value + 2 3 } 4 5 const number22: number = plus2('20') // '202' ` ` ` `

Slide 25

Slide 25 text

型と値がずれる strictNullChecks: false だと undefined が消えたりする @_kimuson 25 1 const elm = document.getElementById('not-existed-id'); // 値: null, 型: HTMLElement 2 elm.style.color = 'red'; // Uncaught TypeError: Cannot read properties of null (reading 'style')

Slide 26

Slide 26 text

すべてのファイルで型が信用できない 型チェックオプションが緩い → 型と値の不一致 新規のコードを含めて TS のメリットが減ってしまう オプションが緩いので、静的に検知できる問題が減る メンテナブルなコードが残りにくい @_kimuson 26

Slide 27

Slide 27 text

がんばらない TypeScript もつらいよ すべてのファイルで型が信用できなくなってしまう 後からの型安全性を高めることが難しい @_kimuson 27

Slide 28

Slide 28 text

後からの型安全性を高めることが難しい 新規で書くコードも型安全性を保証できない 型安全性を高める = 型チェックオプションを硬くするこ と( strict: true ) 移行時に避けた大量のエラーを直す作業が必要 @_kimuson 28 ` `

Slide 29

Slide 29 text

じゃあどうするのか @_kimuson 29

Slide 30

Slide 30 text

メリハリのある TypeScript @_kimuson 30

Slide 31

Slide 31 text

メリハリのある TypeScript 「型チェックの範囲を狭める」アプローチ コメントで行・ファイル単位で型エラーを無視できる 移行時は型チェックオプションは硬いまま エラーになるところは型エラーを無視 @_kimuson 31

Slide 32

Slide 32 text

ts-expect-error: 行単位の型エラー無視 @_kimuson 32 次の行の型エラーは無視される 1 const elm /* :HTMLElement | null */ = document.getElementById('not-existed-id'); 2 3 elm.style.color = 'red'; // Object is possibly 'null'.

Slide 33

Slide 33 text

ts-nocheck: ファイル単位の型エラー無視 @_kimuson 33 コメントがあるファイルの型エラーは無視される 1 2 const elm /* :HTMLElement | null */ = document.getElementById('not-existed-id'); 3 elm.style.color = 'red'; // Object is possibly 'null'. 4 5 parseInt(elm)

Slide 34

Slide 34 text

なにが嬉しいのか @_kimuson 34

Slide 35

Slide 35 text

型安全性にメリハリがつく 目印 型の 信用 度 静的解析 心持ち @ts- nocheck 低 無 補完が効きやすいだけの JavaScript @ts- expect- error 中 有(型の信用度が低いので一部で はあるが検知できる) 型は間違ってる可能性もあるから疑いつつ使う (静的に検知できる問題も多い) 上記が存在 しない 高 有 型を全面的に信用することで恩恵を最大限受けら れる @_kimuson 35

Slide 36

Slide 36 text

運用とともに型安全性が向上させやすい 新規で書くコードは型安全性なコードに 型安全性が徐々に向上させやすい ts-nocheck, ts-expext-error を外すだけ @_kimuson 36

Slide 37

Slide 37 text

実際に弊社でリプレースを行った結果 新規でのコーディングでは TS の恩恵が大きい 一から構築した TypeScript とさほど遜色ない TS の恩恵を大きく受けながら開発できた 型安全性は順調に向上中 @_kimuson 37

Slide 38

Slide 38 text

型安全性は順調に向上中 @_kimuson 38

Slide 39

Slide 39 text

がんばらない vs メリハリのある 「がんばらないTypeScript」は移行コストを下げる まずは移行したほうが良い 移行後に 型で 困ることが少ない 「メリハリのあるTypeScript」は運用に適す 移行の難易度・工数はあがる TypeScript の恩恵を受けやすい @_kimuson 39

Slide 40

Slide 40 text

まとめ TypeScript にすると安全性・開発体験あがっていいよ プロジェクトに合わせて「がんばらないTypeScript」か 「メリハリのあるTypeScript」で @_kimuson 40

Slide 41

Slide 41 text

ご清聴ありがとうございました @_kimuson 41