Closed11

実践TypeScript(BFFとNext.js&Nuxt.jsの型定義)を読んで

umiushiumiushi

第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なため拡張が自由すぎて危険
umiushiumiushi

第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構文で読み込んだモジュールには型推論は働かない
umiushiumiushi

第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
nullundefined型を取り除くアサーション

umiushiumiushi

第5章 TypeScriptの型システム

型の互換性
Structual Subtyping(構造的部分型)

  • 関数型の互換性
    • 引数の型に互換性があるかどうかで判定
    • より多い引数により少ない引数を代入可能

宣言空間
空間が異なれば同じシグネチャーの宣言が可能

  • Value宣言空間
    • 変数や関数の宣言が割り当たる
  • Type宣言空間
    • interfaceやtype aliasの宣言が割り当たる
  • Namespace宣言空間
    • 空間内で同名の定義は結合される(=>モジュール拡張)
umiushiumiushi

第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>
umiushiumiushi

第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>>
umiushiumiushi

第9章 ExpressとTypeScript

Express
https://expressjs.com/ja/

ts-node-dev

  • tsをjsにコンパイルせずにnode上で実行可能にしてくれる
  • nodeのホットリロードをしてくれる
  • クライアントとサーバーでスキーマ定義を型(規約)として共有して、ミスをコンパイルエラーで検知できるようにしましょう

  • Business Domainの世界を型システムで表現しましょう

redis
key-value形式のDB

https://redis.io/

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') // <= コンパイルエラー
umiushiumiushi

第10章 Next.jsとTypeScript

Next.js
https://nextjs.org/

  • _document
    サーバーサイドで実行される共通処理を定義するような場所
  • _app
    各ページコンポーネントに共通する処理を定義するような場所
  • _error
    カスタムエラーページの実装をする場所

Next with Redux
ReduxをNextに導入すると、pagesディレクトリに含まれるページコンポーネントでgetInitialProps関数の引数としてstoreを受け取れるようになる

※ライブラリが提供しているデフォルトの型に対してピンポイントでモジュール型を拡張してあげることでプロジェクト固有の型知識を注入できる

ライブラリのライフサイクル全体でプロジェクト固有の型知識を都度importせずとも扱えるようになる

このスクラップは2022/05/14にクローズされました