Last active
August 19, 2023 14:09
-
Star
(104)
You must be signed in to star a gist -
Fork
(0)
You must be signed in to fork a gist
-
-
Save mizchi/9e71569f72187af749adfecea49fb38a to your computer and use it in GitHub Desktop.
Revisions
-
mizchi revised this gist
Jul 10, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -315,7 +315,7 @@ circleci ない人は husky などで頑張って "module": "es2015", "esModuleInterop": true, // 段階的に有効化 "alwaysStrict": false, // "use strict" 有効化 "strictNullChecks": false, // null|undefined 厳格化 "noImplicitAny": false // 推論不可能なときにアノテーション必須に } -
mizchi revised this gist
Jul 10, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -342,7 +342,7 @@ circleci ない人は husky などで頑張って - 型付けるとコスパ良い順 - ORM 周り (node.js) - API レスポンスの返り値や、それを使う周辺 - Model/Store 層(redux/vuex) - View の入力(React/Vue Props) - View ステート層(React state / Vue data) -
mizchi revised this gist
Jul 10, 2019 . 1 changed file with 2 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -417,19 +417,9 @@ export class StateManager<A, B, C, D, E, F> {...} ## 難しい型を書いてしまう - 例: react-redux@connect のヤバさ - ライブラリ作者は抽象度が必要 <=> アプリケーション内で難しい型は割れ窓 - 同僚に any にされる - **any になる可能性があるものは、 any になる** --- -
mizchi revised this gist
Jul 10, 2019 . 1 changed file with 24 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -27,6 +27,8 @@ mizchi / TypeScript Meetup 2 推論あればそうでもなかった。むしろドキュメントとして有用。 でも動的が流行ったあとだから、一旦は漸進的型付けで行く」 --- ## この発表の目的 - TypeScript を導入しない言い訳を全部潰す @@ -369,9 +371,9 @@ circleci ない人は husky などで頑張って ## とにかく静的解析を強化 - `@typescripc-eslint` - 色々やったが `@typescripc-eslint/no-ununsed-vars` が一番効く - `prettier` - jest のカバレッジ機能 + React SSR でスナップショットなど --- @@ -381,6 +383,7 @@ circleci ない人は husky などで頑張って - Generics, Union Type, Promise 表現 - 自作ライブラリの `.d.ts` 書く - フレームワークごとのイディオム - 個人的に Scala の経験が生きた --- @@ -411,6 +414,25 @@ export class StateManager<A, B, C, D, E, F> {...} --- ## 難しい型を書いてしまう - 例: react-redux@connect のヤバさ - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-redux/index.d.ts - ライブラリ作者は抽象度が必要 <=> アプリケーション内で難しい型は割れ窓 - 難しい型は使いこなせなくて、同僚に any にされる --- ## 難しい型を書いてしまう: 対策 ```ts // アプリケーションコンテキストで決め打ちしてしまう export const myConnect: <Mapped> (fn: (state: MyRootState) => Mapped) => = (fn) => ReactRedux.connect(fn); ``` --- ## 潰れる Action ```ts -
mizchi created this gist
Jul 10, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,460 @@ # 非破壊 TypeSctript mizchi / TypeScript Meetup 2 --- ## About - mizchi / 竹馬光太郎 - フロントエンドと Node.js - 株式会社プレイド 2019/7~ - Frontend Ops 周り - DOM 差分監視して色々 --- ## はじめに - Modern JS ≒ TypeScript の時代になった - なので型を書け --- ## これまでの(歴史の)あらすじ! 「Java の静的型付けが大変で、反動で動的なのが流行ったけど、 推論あればそうでもなかった。むしろドキュメントとして有用。 でも動的が流行ったあとだから、一旦は漸進的型付けで行く」 ## この発表の目的 - TypeScript を導入しない言い訳を全部潰す - そのために痛みがない導入・運用を提示する --- ## Outline - TS の型アノテーションとはなにか? - 導入編 - 発展編 - アンチパターン --- ## TS の型アノテーションとはなにか? --- ## TS の型アノテーションとは何「ではない」か - メモリ確保量を決めるもの、ではない - TS の型はインターフェースしか知らない - 実行時の挙動を決めるもの、ではない - TS の型宣言はランタイムに関与しない --- ```ts // TS は ArrayBuffer であることを知っているが // VM で確保されるメモリに興味がない const buf: ArrayBuffer = new ArrayBuffer(8); ``` --- ## それでも、なぜ型アノテーションを書くのか - 人間のためのインターフェース宣言 - 実行可能な Lint - それらによくコード品質の向上・レビューコストの削減 --- ### TypeScript はコンパイラというより、「**型を検証可能な Lint ツール**」 --- ## TypeScript Compiler(tsc) の役割 主機能 - 型の静的検査 - エディタへの情報提供 - Language Server Protocol おまけ機能 (babel で代替可能) - 型アノテーションの除去 - ES2015 => ES5 --- ## 型は JIT に優しい - V8 等の JIT(実行時最適化)は、何度も実行される処理のデータのシェイプ(≒ 型)を仮定する - 仮定が崩れると速度が落ちる(deopt) - 厳密には色々あるが、綺麗な型がつく方が高速な傾向 --- ## 導入編 --- ## TS 導入最小ステップを考える --- ## コンパイラとして使う ``` $ npm install typescript webpack webpack-cli ts-loader --save-dev ``` 最小 `tsconfig.json` ```js { "compilerOptions": { "target": "es5", "module": "es2015", // ESM は webpack が変形する "esModuleInterop": true } } ``` --- ## 最小 webpack.config.js ```js module.exports = { resolve: { extensions: [".ts"] }, module: { rules: [ { test: /\.ts$/, use: [ { loader: "ts-loader", options: { transpileOnly: true // 型チェックしない!!! } } ] } ] } }; ``` --- ## 最小 TS 用 Hello World src/index.ts ```ts // 型はわざと間違ってる。アノテーションが取り除かれることを確認する const text: number = "World"; console.log(`Hello, ${text}`); ``` --- ## 最初に覚えるコマンド ```bash $ npx webpack # build only # => dist/main.js $ npx tsc -p . --noEmit # type check only ``` エラーを確認 ``` src/index.ts:1:7 - error TS2322: Type '"World"' is not assignable to type 'number'. 1 const text: number = "World"; ``` --- ## 最初にやること - 「コードを修正せずに」自分の `.js` を `.ts` にする - 「CI で型違反を検査せずに」ふるまいを手動/ユニットテストで確認 --- ## なぜこうなるか - 初手はコンパイラとしての機能を保証する - どうせ最初は型チェックを通せない - 後々効く: コンパイルと型チェックの分離で高速化 - どうせ後でも IDE で型違反を見る - どうせ後でも CI で型チェックする (awesome-typescript-loader やめとけ) --- ## ライブラリ型定義をいれる ``` $ npm install --save-dev @types/<pkg-you-want> ``` 自分がほしいやつを一通り叩いてみる (一部のライブラリは TS 型定義を同梱) ```ts // npm install --save @types/lodash import { range } from "lodash"; range(3); ``` --- ## ライブラリ型定義を潰す たぶん全部の型定義ファイルは揃わないので一旦潰す ```typescript // src/decls.d.ts declare module "xxx"; // xxx の型定義がない or きつい // ちょっと頑張る declare module "yyy" { export function foo(input: string): number; } ``` --- ## **any** 祭り - `tsc -p . --noEmit` で落ちた場所を修正していく - 読み下しながら自明な範囲で `number` や `string` を付与する - わからなかったら `any` や `@ts-ignore` で無視 - **ロジックを変更しない!!!!** --- ## 潰されるコード ```ts function genMagicId(): number | string { // -- なんかやばいコード -- // @ts-ignore return super_magical_func(); } ``` - 本当に守りたいのは関数の入出力 - 暗黒面に落ちるぐらいなら any で全部潰す --- ## 慣用句: as any as ... ```ts const foo: Foo = (foobar as any) as Foo; ``` - 推論過程が導けない場合に仕方なく書くもの - 多用厳禁 --- ## 中身を無視して型定義 - src/foo.js # 中は気にしないことにする - src/foo.d.ts # 外から型を指定する ```ts // foo.d.ts export const hoge: <T>(t: T) => Promise<T>; ``` --- ## CI を通す - パスしたら CI で型チェックを流す `.circleci/config.yml` ```yaml steps: ... - run: npx tsc -p . --noEmit ``` circleci ない人は husky などで頑張って --- ## 結果: ガバガバ状態で導入完了 --- # 発展編 --- ## がんばる tsconfig.json ```js { "compilerOptions": { "target": "es5", "module": "es2015", "esModuleInterop": true, // 段階的に有効化 "strict": false, // "use strict" 有効化 "strictNullChecks": false, // null|undefined 厳格化 "noImplicitAny": false // 推論不可能なときにアノテーション必須に } } ``` ↓ ほど難易度高い --- ## 大事なこと(これだけ覚えて帰って!) - 型とロジックを同時に修正しない - 推論などで発見すると嬉しくて修正したくなる - => 確認の工数がかかる - => マージされない - => 治安悪い状態が続く - 別 Issue にしましょう --- ## 型のコスパ感覚 - 型付けるとコスパ良い順 - ORM 周り (node.js) - API レスポンスの返り値や、それを使う周辺 - モデル層(redux/vuex) - View の入力(React/Vue Props) - View ステート層(React state / Vue data) --- ## テストのコスパ感覚 - 型アノテーション増やす > 単体テスト - テスト書かないでいいわけではないが型書くのを優先 - 単体テストで型の効果を補強するイメージ - そもそもフロントエンドのテストはやりづらい(ので静的解析優先) --- ## ドメイン層を守る - 本当に守りたいのは(たぶん)データベースと API - スキーマ定義から型定義生成ツールがあると良い - grpc => .d.ts - graphql => .d.ts - jsonschema => .d.ts - フレームワークのメタデータから自作 --- ## とにかく静的解析を強化 - `@typescripc-eslint` - 何にせよ `no-ununsed-vars` が効く - `prettier` - jest のカバレッジ機能 --- ## TS 筋を鍛える - DeepDive https://typescript-jp.gitbook.io/deep-dive/ - Generics, Union Type, Promise 表現 - 自作ライブラリの `.d.ts` 書く - フレームワークごとのイディオム --- ## アンチパターン集 --- ## typeof initialState ```ts const initialState = {...}; export type State = typeof initialState; ``` - 型とインスタンスの従属関係が逆 - 本当に管理したいのは潜在的に State のとりうる状態 --- ## 型引数多すぎ問題 ```ts export class StateManager<A, B, C, D, E, F> {...} ``` - 人間が管理できる型引数はたぶん 2 つぐらいまで - ライブラリ作者は 3 つ、アプリケーション内なら 2 つまでが感覚的なセーフライン --- ## 潰れる Action ```ts export type Action = { type: string; payload: any; }; ``` - 間違っちゃいないんだけど union type でもっと詳細に書ける --- ## namespace と import foo = require(...) ```ts import module = require("module"); ``` - ESModules 導入以前の独自モジュールシステム。新規で書く場合には不要 --- ## キャストによる治安悪化 ```ts const id: string = number_or_string as string; ``` - 本当に型の契約が守れてるかが自己責任 - 新規にコードを書く際は Type Refinements (if /switch による union type 絞り込み) を優先 --- ## 議論用のテーマ: どう思いますか?  --- ## 参考 - [TypeScript 入門以前ガイド - mizchi's blog](https://mizchi.hatenablog.com/entry/2018/10/03/195854) --- ## おわり