実践TypeScript(BFFとNext.js&Nuxt.jsの型定義)を読んで
第1章 開発環境と設定
最新のTSのインストール
npm i typescript@next
第2章 TypeScriptの基礎
種類 | 表現 | 備考 |
---|---|---|
boolean型 | const value: boolean |
|
number型 | const value: number |
|
string型 | const value: string |
|
array型 |
const value: string[] const value: Array<string>
|
|
tuple型 | const value: [string, number] |
|
any型 | const value: any |
|
unknown型 | const value:unkown |
型安全なany=代入はできるが、利用はコンパイルエラー |
void型 | function demo(): void |
null か unidefinedのみ代入可能 |
null型 | const value: null |
全ての型のサブタイプ |
unidefined型 | const value: unidefined |
全ての型のサブタイプ |
never型 | function demo(): never |
値が発生しないことを表現 |
object型 | const value: { f1: string, f2: number} |
Intersection Types(交差型)
複数の型を一つに結合したもの
type intersection = A & B
Union Types(共用型)
複数の型のうち一つの型が成立すればよいもの
type union = A | B
型によるnullable表現 T | null
Literal Types
文字列、数値、真偽値のうち取り得る値を定めて定数のように扱える
type SL = 'HOGE' | 'FUGA'
type NL = 1 | 10
type BL = true
enum
- TypeScriptの機能でJavaScriptの機能でないため利用は控えた方がよい
- open endedなため拡張が自由すぎて危険
第3章 TypeScriptの型推論
const/letの型推論
- constは再代入不可=>リテラル
- let=>プリミティブ
※明示的な型アサーションのない変数を再代入可能な変数に代入し直すと型推論はプリミティブになる
Arrayの型推論
// 厳格にしたい場合
const array = ['1' as '1', '2' as '2'] // => ('1' | '2')[]
Tupleの型推論
// タプルは明示的に定義しなければ型推論は働かない(配列との区別がつかない)
const tuple = ['1', '2'] as [string, string]
objectの型推論
リテラルタイプを維持したい場合はアサーションが必要(プロパティは再代入可能だから)
object {
value: '1' as '1'
}
その他
- require構文で読み込んだモジュールには型推論は働かない
第4章 TypeScriptの型安全
オプショナルな引数
property? => 自動的にundefined型
が追加される
TypeGuard
型を絞り込むことで型推論が状況に応じて絞り込みを行ってくれる
- typeof
- in type
- instanceof
- タグ付きUnion
- is type
Weak Type
全てのプロパティがオプショナルな型
※但し、objectリテラルを直接記述する場合(変数を介さない場合)ExceccPropertyChecksと呼ばれ厳格に一致していないとエラーになる
読み込み専用
- プロパティを読み込み専用にしたい
readonly prop: type
- 型全体を読み込み専用にしたい
Readonly<Type>
※Object.freezeの適用された値はReadonly型として推論される
インデックスシグネチャー
- 動的なプロパティ追加を型付け
type User = {
name: string
[k: string]: any
}
※インデックスシグネチャーの型はオブジェクト内でトップレベル宣言されている全てのプロパティの型を満たさなければならない
- プロパティの名称の型付け
type Question = 'hoge' | 'fuga'
type User = {
name: string
question: { [K in Question]?: Answer }
}
const assertion
ハードコードしたリテラルをそのままリテラル型として扱える
(※いちいちasで全ての値をアサーションしなくて良い上に、Widening Literal Typesも抑制してくれる)
const tuple = [`test`, 100] as const
export object {
prop1: ...,
prop2:...
} as const
non-null assertion
null
とundefined
型を取り除くアサーション
第5章 TypeScriptの型システム
型の互換性
Structual Subtyping(構造的部分型)
- 関数型の互換性
- 引数の型に互換性があるかどうかで判定
- より多い引数により少ない引数を代入可能
宣言空間
空間が異なれば同じシグネチャーの宣言が可能
- Value宣言空間
- 変数や関数の宣言が割り当たる
- Type宣言空間
- interfaceやtype aliasの宣言が割り当たる
- Namespace宣言空間
- 空間内で同名の定義は結合される(=>モジュール拡張)
第6章 TypeScriptの高度な型
Generics
- 初期Genericsの付与が可能
interface Box<T = string> {
value: T
}
Conditional Types
型の条件分岐式を使って、型に制約を設けたり、部分型の抽出が可能
** Utility Types**
-
Readonly型
Object型のプロパティを全てreadonlyに変換した、新しい型を生成する -
Partial型
Object型のプロパティを全てoptionalに変換した、新しい型を生成する -
Required型
Object型のプロパティからoptionalを取り除いた、新しい型を生成する -
Record型
Record<'name', Type>
指定した名称で新しいObject型を生成する -
Pick型
Pick<Type, 'name'>
指定した名称のプロパティ型を抽出した新しいObject型を生成する -
Omit型
Omit<Type, 'name'>
指定した名称のプロパティ型を取り除いた新しいObject型を生成する -
Exclude型
Exclude<T, U>
<T>から<U>と互換性のある型を全て取り除いた新しい型を生成する -
Extract型
Extract<T, U>
<T>から<U>と互換性のある型のみを抽出した新しい型を生成する -
NonNullable型
NonNullable<T>
<T>からnullおよびundefinedを除いた新しい型を生成する
その他役立ちそうな応用
- 再起的な型変換処理
type Unbox<T> = T extends {[k: string: infer U} ? U
: T extends (infer U)[]? : U
: T
type isPrimitive<T> = T extends Unbox<T> ? T : never
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends isPrimitive<T[P]>
? T[P]
: DeepReadonly<T[P]>
}
- NonEmptyList
type NonEmptyList<T> = [T, ...T[]]
- PickSet
type PickSet<T> = T extends Set<infer I> ? I : never
const set = new Set([1, 2] as const)
type setValue = PickSet<typeof set>
- PickMap
type PickMapKey<T> = T extends Map<infer K, any> ? K : never
type PickMapValue<T> = T extends Map<any, infer V> ? V : never
const map = new Map([
[0, 'foo'],
[1, 'bar']
] as const)
type mapKeys = PickMapKey<typeof map>
type mapValues = PickMapValue<typeof map>
第7章 ReactとTypeScript
コンポーネント開発は、表示するデータ型の考察から始まる
コンポーネント粒度はDOMツリー構造と取り扱うデータ構造から判断する(型の粒度とコンポーネントの粒度も合わせた方が良い)
状態管理
2019/02~(16.8) React Hooksの登場
Fluxアーキテクチャー
useReducerにActionの型安全を持ち込む
type ReturnTypes<T> = {
// Mapped Typeでobjectを走査=>値を取ってそれが何かしらの関数である場合は組み込みユーティリティー型で関数の戻り型を取得
[K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> : never
}
// オブジェクトの値をUnionTypeとして取得する
type Unbox<T> = T extends { [k in keyof T]: infer U } ? U : never
export type CreatorsToActions<T> = Unbox<ReturnTypes<T>>
第8章 Vue.jsとTypeScript
関心対象外のためスキップ
第9章 ExpressとTypeScript
Express
ts-node-dev
- tsをjsにコンパイルせずにnode上で実行可能にしてくれる
- nodeのホットリロードをしてくれる
-
クライアントとサーバーでスキーマ定義を型(規約)として共有して、ミスをコンパイルエラーで検知できるようにしましょう
-
Business Domainの世界を型システムで表現しましょう
redis
key-value形式のDB
Expressサーバーの開発サーバーとしての拡張
- webpack-dev-middleware
- webpack-hot-middleware
Hot Module Replacement (HMR)
画面の再描画すること無しにJSの変更をブラウザに適用する仕組み
Lookup Type
- keyof
オブジェクトの全てのプロパティを、文字列リテラル型のユニオン型(合併)で取得できる
export interface EndPoint {
point_1: {}
point_2: {}
}
const demoMethod = <P extends keyof EndPoint>(path: P) => {
console.log(path)
}
demoMethod('point_1')
demoMethod('point_3') // <= コンパイルエラー
第10章 Next.jsとTypeScript
Next.js
- _document
サーバーサイドで実行される共通処理を定義するような場所 - _app
各ページコンポーネントに共通する処理を定義するような場所 - _error
カスタムエラーページの実装をする場所
Next with Redux
ReduxをNextに導入すると、pagesディレクトリに含まれるページコンポーネントでgetInitialProps関数の引数としてstoreを受け取れるようになる
※ライブラリが提供しているデフォルトの型に対してピンポイントでモジュール型を拡張してあげることでプロジェクト固有の型知識を注入できる
ライブラリのライフサイクル全体でプロジェクト固有の型知識を都度importせずとも扱えるようになる
第11章 Nuxt.jsとTypeScript
関心対象外のためスキップ