フロントエンドエンジニアをやっています。
頑張るから読んでほしい。

実践 TypeScriptの読書MEMO1

最近、実践 TypeScriptを読んでいるので読書メモです。

実践TypeScript ~	BFFとNext.js&Nuxt.jsの型定義~

実践TypeScript ~ BFFとNext.js&Nuxt.jsの型定義~

  • 作者:吉井 健文
  • 発売日: 2019/06/26
  • メディア: 単行本(ソフトカバー)

私の知らなかったことをたくさん書いてます。

どんな本

  • TypeScript特有の知識を体系的に学ぶための1冊
    • 集中してTypeScriptの型定義を深く学べる
  • 1部は基礎知識、2部は理解を深める
  • TypeScriptを導入するメリット
    • 分割されたモジュール同士の依存関係を担保する
    • 型システムが、バックエンドからフロントエンドの末端のviewまで一環して不整合ないデータの橋渡しを実現する

目的

  • TypeScriptの型定義きちんと理解したい
    • 型定義ははじめてなので…
  • 最終的にTypeScriptを実践的に使えるようになりたい
  • 先輩のTypeScriptのコードをちゃんと理解できるようになりたい
  • 4章5章をちゃんと読んでTypeScriptのコンパイルエラーの解決慣れしたい

1. 開発環境と設定

1-1. tsconfig.json

TypeScriptコンパイラーの設定ファイル。 tsc --initで作成

tsconfig.json

{
  // teconfig.jsonの初期値
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
}

target

トランスパイル後のECMAScriptのターゲットバージョン。

  • es5
  • es2015
  • esnext

module

コード生成モジュールを指定

  • commonjs
  • amd
    • define()に配列でモジュール名を指定する

strict

型の厳密さを一括して指定

一括で有効になるのは以下の指定

  • noImplicitAny
  • noImplicitThis
  • alwaysStrict
  • strictBindCallApply
  • strictNullChecks
  • strictFunctionTypes
  • strictPropertyInitialization

1-2. 型宣言ファイルの出力

関数定義などの型を通達するために、型宣言ファイル(.d.ts)が利用できる

.d.tsいつ使うんだろう🤔

  • 型情報が消えしまう
  • .d.tsは型定義が分かる
  • JavaScriptで書かれたライブラリには型情報がない…
  • ライブラリ等で使うときは型定義ファイルがあれば、みんなが幸せになれる

1-3. ライブラリの型定義を利用する

npmで配信されているライブラリには、TypeScriptの型定義が存在するものとしないものがある。

DefinitelyTyped

TypeScript型定義ファイル(dts)を用意していないJSライブラリのための型定義ファイル集 @typesで始まるパッケージ

2. TypeScriptの基礎

TypeScriptのもっとも基本的な用語の呼称・利用方法についての章。

2-1. JavaScriptの課題

1. 引数に数値として扱うことのできない値を渡してしまいNaNが表示されてしまう

  • 引数を型注釈で制約する
function expo2(amout: number){
  return amout ** 2
}

2. 計算の途中、数値が文字列に変換されてしまいNaNが返ってくる

  • 関数戻り値に型注釈(アノテーション)を付与。明示的に戻り値を制約。型注釈と定義内容に型不整合がある場合はコンパイルエラーを得ることができる。
function fee(amount): number {
    return amount * 1.4
}

2-2. 基本の型

(私の知らなかった型をまとめてます。)

  • tuple型
    • 固定数の要素がわかっている配列を表現できる
    • 配列の要素ごとに型が違うデータ型が定義できる。
      • let x: [string, number]
  • any型
    • 型の不明な変数を扱うことがある場合に使用することで型チェックを無効にし、コンパイルを通過させる
    • TypeScriptの恩恵を受けることはできないので、できる限りany型が現れないコードを書き、型安全なプロジェクトを目指す。
  • unknown型
    • any型に似ているが、型安全なanyを表したいときに利用する
  • void型
    • any型の反対のようなもの。型が全くないことを表す。
    • 一般に、値を返さない関数の戻り値として利用
  • never型
    • 発生し得ない値の型を表す
    • 返り値が無いことを表すvoid型とは異なり、そもそも関数が正常に終了して値が返ってくるということがあり得ない場合
function func(): never {
   throw new Error('Hi');
 }
 const result: never = func();
  • object型
    • 非プリミティブ型
    • {}を使った型表現ではエラーを得ることができない

2-3. 高度な型

  • Intersection Types
    • 複数の型を一つに結合する
  • Union Types
    • Union Types(共用体)は複数の型のうち一つの型が成立することを示している。
      • let value: boolean | number | string
        • array型を含む要素をUnion Typesにする場合
      • let numberOrString: (number | string)[]
        • Nullable型を表現できる
      • let nullableString: string | null
  • Literal Type
    • String Literal Types
    • 文字列に必要な正確な値を指定できる
    • let users: 'Taro' | 'Jiro'| 'Hanako' (Union Typesと併用)
    • Numeric Literal Types(数値リテラル)
    • Boolean Literal Types(真偽値リテラル)

2-5. アサーション

任意の型に変換することができる。

アサーションは2種類の書き方がある。

  • <変換したい型>値
  • 値 as 変換したい型
let text: any = "this is a string";
let textLength: number = (<string>text).length;
let text: any = "this is a string";
let textLength: number = (text as string).length;

↑ 文字列の文字数を取得するために string に変換して length を呼び出している

<>ではJSXタグとの区別が曖昧になるため、非推奨

2-6. 列挙型

enumを使用すると、列挙型を定義できる。 列挙している属性以外が入ってくると怒ってくれる

3. TypeScriptの型安全

バグを減らすことはTypeScriptを導入する大きな理由の一つであり、

TypeScriptを使いこなすようになるほど、絞り込むという作業がバグを減らす上でいかに重要な作業かということに気づくらしい。

3-1. 制約による型安全

  • アノテーションを用いると、引数や変数定義において、誤った値の代入を防げる。
  • null, undefined
    • 早期return(ガード節、Type Guard)
  • Weak Type
    • すべてのプロパティがオプショナルな型

3-2. 抽象度による型安全

  • 抽象的な型は広く値を受け付けることができる
  • 詳細な型は担保された制約の下で処理を安全に展開できる
  • 抽象的な型・詳細な型がどのようなものであるかを理解し、抽象度をコントロールすることが必要
  • キャスト
    • 変数の型を別の型へ変換すること
  • ダウンキャスト
    • 抽象的な型から詳細な型を付与すること(互換性があるときのみ可能、互換性がない場合は成立しない)
  • アップキャスト
    • 抽象度をあげる
  • インデックスシグネチャ
    • 任意のプロパティを動的に追加することが可能
  • 危険な型の付与
    • Non-null assertion
    • double assertion

4. TypeScriptの型システム

バグを減らすことはTypeScriptを導入する大きな理由の一つ。

TypeScriptを使いこなすようになるほど、絞り込むという作業がバグを減らす上でいかに重要な作業かということに気づくらしい。

4-1. 型の互換性

any型の互換性

  • どんな型にも宣言・代入できる。
  • any型は、どのように扱おうとも危険な型

unknown型の互換性

  • unknown型は、どんな型の値も受け入れることができるTopTypeであり、型の中でもっとも抽象的な型。
    • any型とは異なり安全に扱うことができる型
  • unknown型の値はどんな値なのか分からないため、できることが制限されている。  - (任意の値を代入できる点はany型と同じ、型アサーション等が無いと利用できない。)

{}型の互換性

  • 型に互換性がありため、いずれもコンパイルエラーにはならない。
  • {}という型はオブジェクト以外も受け付けてしまう。(ただし、undefinedとnullはだめ)
  • プリミティブ型は{}型のサブタイプ

5. TypeScriptの高度な型

Generics

  • Genericsを用いると、型の決定を遅延できる。
  • Genericsが割り当てられた型定義に対し、< >の内側に型指定をすることで、導かれる型推論を可変にできる。

基本的な付与

  • Genericsを利用する型を宣言する場合、型名称に続いて<T>のようにT型をエイリアスとして指定する。
  • 慣習的にT, U, Kなどの型エイリアス名称が利用されることが多い。

Genericsを利用する型の宣言

interface Box<T> {
    value: T
}
const box0: Box = { value: 'test' } // Error! Genericsを指定していない
const box1: Box<string> = { value: 'test' }
const box2: Box<number> = { value: 'test' }// Error! Number型ではない





実践TypeScriptの読書MEMO2に続きます。