Instantly share code, notes, and snippets.
Last activeAugust 19, 2023 14:09
Save mizchi/9e71569f72187af749adfecea49fb38a to your computer and use it in GitHub Desktop.
mizchi / TypeScript Meetup 2
- mizchi / 竹馬光太郎
- フロントエンドと Node.js
- 株式会社プレイド 2019/7~
- Frontend Ops 周り
- DOM 差分監視して色々
- Modern JS ≒ TypeScript の時代になった
- なので型を書け
「Java の静的型付けが大変で、反動で動的なのが流行ったけど、推論あればそうでもなかった。むしろドキュメントとして有用。でも動的が流行ったあとだから、一旦は漸進的型付けで行く」
- TypeScript を導入しない言い訳を全部潰す
- そのために痛みがない導入・運用を提示する
- TS の型アノテーションとはなにか?
- 導入編
- 発展編
- アンチパターン
- メモリ確保量を決めるもの、ではない
- TS の型はインターフェースしか知らない
- 実行時の挙動を決めるもの、ではない
- TS の型宣言はランタイムに関与しない
// TS は ArrayBuffer であることを知っているが// VM で確保されるメモリに興味がないconstbuf:ArrayBuffer=newArrayBuffer(8);
- 人間のためのインターフェース宣言
- 実行可能な Lint
- それらによくコード品質の向上・レビューコストの削減
主機能
- 型の静的検査
- エディタへの情報提供 - Language Server Protocol
おまけ機能 (babel で代替可能)
- 型アノテーションの除去
- ES2015 => ES5
- V8 等の JIT(実行時最適化)は、何度も実行される処理のデータのシェイプ(≒ 型)を仮定する
- 仮定が崩れると速度が落ちる(deopt)
- 厳密には色々あるが、綺麗な型がつく方が高速な傾向
$ npm install typescript webpack webpack-cli ts-loader --save-dev最小tsconfig.json
{"compilerOptions":{"target":"es5","module":"es2015",// ESM は webpack が変形する"esModuleInterop":true}}
module.exports={resolve:{extensions:[".ts"]},module:{rules:[{test:/\.ts$/,use:[{loader:"ts-loader",options:{transpileOnly:true// 型チェックしない!!!}}]}]}};
src/index.ts
// 型はわざと間違ってる。アノテーションが取り除かれることを確認するconsttext:number="World";console.log(`Hello,${text}`);
$ 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 型定義を同梱)
// npm install --save @types/lodashimport{range}from"lodash";range(3);
たぶん全部の型定義ファイルは揃わないので一旦潰す
// src/decls.d.tsdeclare module"xxx";// xxx の型定義がない or きつい// ちょっと頑張るdeclare module"yyy"{exportfunctionfoo(input:string):number;}
tsc -p . --noEmitで落ちた場所を修正していく- 読み下しながら自明な範囲で
numberやstringを付与する - わからなかったら
anyや@ts-ignoreで無視 - ロジックを変更しない!!!!
functiongenMagicId():number|string{// -- なんかやばいコード --//@ts-ignorereturnsuper_magical_func();}
- 本当に守りたいのは関数の入出力
- 暗黒面に落ちるぐらいなら any で全部潰す
constfoo:Foo=(foobarasany)asFoo;
- 推論過程が導けない場合に仕方なく書くもの
- 多用厳禁
- src/foo.js # 中は気にしないことにする
- src/foo.d.ts # 外から型を指定する
// foo.d.tsexportconsthoge:<T>(t:T)=>Promise<T>;
- パスしたら CI で型チェックを流す
.circleci/config.yml
steps:... -run:npx tsc -p . --noEmit
circleci ない人は husky などで頑張って
{"compilerOptions":{"target":"es5","module":"es2015","esModuleInterop":true,// 段階的に有効化"alwaysStrict":false,// "use strict" 有効化"strictNullChecks":false,// null|undefined 厳格化"noImplicitAny":false// 推論不可能なときにアノテーション必須に}}
↓ ほど難易度高い
- 型とロジックを同時に修正しない
- 推論などで発見すると嬉しくて修正したくなる
- => 確認の工数がかかる
- => マージされない
- => 治安悪い状態が続く
- 推論などで発見すると嬉しくて修正したくなる
- 別 Issue にしましょう
- 型付けるとコスパ良い順
- ORM 周り (node.js)
- API レスポンスの返り値や、それを使う周辺
- Model/Store 層(redux/vuex)
- View の入力(React/Vue Props)
- View ステート層(React state / Vue data)
- 型アノテーション増やす > 単体テスト
- テスト書かないでいいわけではないが型書くのを優先
- 単体テストで型の効果を補強するイメージ
- そもそもフロントエンドのテストはやりづらい(ので静的解析優先)
- 本当に守りたいのは(たぶん)データベースと API
- スキーマ定義から型定義生成ツールがあると良い
- grpc => .d.ts
- graphql => .d.ts
- jsonschema => .d.ts
- フレームワークのメタデータから自作
@typescripc-eslint- 色々やったが
@typescripc-eslint/no-ununsed-varsが一番効く
- 色々やったが
prettier- jest のカバレッジ機能 + React SSR でスナップショットなど
- DeepDivehttps://typescript-jp.gitbook.io/deep-dive/
- Generics, Union Type, Promise 表現
- 自作ライブラリの
.d.ts書く - フレームワークごとのイディオム
- 個人的に Scala の経験が生きた
constinitialState={...};exporttypeState=typeofinitialState;
- 型とインスタンスの従属関係が逆
- 本当に管理したいのは潜在的に State のとりうる状態
exportclassStateManager<A,B,C,D,E,F>{...}
- 人間が管理できる型引数はたぶん 2 つぐらいまで
- ライブラリ作者は 3 つ、アプリケーション内なら 2 つまでが感覚的なセーフライン
- 例: react-redux@connect のヤバさ
- ライブラリ作者は抽象度が必要 <=> アプリケーション内で難しい型は割れ窓
- 同僚に any にされる
- any になる可能性があるものは、 any になる
exporttypeAction={type:string;payload:any;};
- 間違っちゃいないんだけど union type でもっと詳細に書ける
importmodule= require("module");
- ESModules 導入以前の独自モジュールシステム。新規で書く場合には不要
constid:string=number_or_stringasstring;
- 本当に型の契約が守れてるかが自己責任
- 新規にコードを書く際は Type Refinements (if /switch による union type 絞り込み) を優先
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
