Open12

Effective TypeScript, 2nd Edition/読書メモ

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

23 エイリアスの使用に一貫性を持たせる

const { prop } = obj;

someFunc(obj)

とするとpropが破壊される可能性がある。obj自体を別の場所で操作されてしまう可能性があるから。

処理は簡潔にかけるのだが。

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

22 タイプの絞り込みを理解する

func A(obj: { value: stirng | number }) {
  if (typeof obje.value === 'number') {
    setTimtoue(() => { obje.value.toFixed() }, 10);
  }
}

これはエラーになる可能性がある。なぜなら、valueがsetTimeout実行前に変更されうるから

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

25 型推論でコンテキストがどのように使われるかを理解する

let lang = 'Japanesel; // string

setLang(lang); // langのunionを求めている場合エラー

langが変更されうるため、型がstringとしてさだまるから?setLang実行時の値から推論されない。
これはconstを使うと改善される

タプル

const a = [1, 10];

function b(value: [number, number]) {}

b(a);

はエラー。aがnumber[]として推測されるから。as const でもエラーは回避できない。
なぜなら具体的すぎるから。readonly[1, 10] として扱われている。

bの型を直すのが良い。

https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAhjAvDA2gRgDQzQBgLrwQyiRQDcAUBQGYCuYwUAluDAEYAUAbnADa0BTAFwwATgLgATcLwCeqMLQC2bAaKyKVavAEoYAbwC+VTnB1kgA

他には

b([10, 20])

と書けば当然OK。inlneな書き方。

Object

同じことが起こる。satisfiesを使うと良い。Object初期化時の具体的な値が保持される。

Callback

function withcallback(c: (a: number, b:number) => void) {};

withcallback((a, b) => { console.log(a + b); })

aとbはnumberとして推論される。callbackを別の場所で定義したらanyとなる。
面倒ならinlineで(使い場所で)定義してしまうといい。

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

25 進化するタイプを理解する

処理の途中で型が進化することがある。any[]がnumber[]になったり、nullがnumber | nullになったりする。

function a() {
    let b = [];

    for (let i = 0; i < 10; i++) {
        b.push(i);
    }

    return b;
}

bはany[]からnumber[]になる

function a() {
    let b = null;

    if (Math.random() > 0.5) {
        b = 'hoge';
    }

    return b;
}

これを避けるには明示的に型注釈を戻り値にいれると良い

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

26 型の流れを助けるために関数型コンストラクトとライブラリを使う

型を考えるとlodashのようなライブラリの採用はメリットがある。ライブラリを使わない場合は方注釈が必要となる

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

27 コールバックの代わりに非同期関数を使用して型フローを改善する

async を使うことで非同期に処理が統一されるため、利用側がシンプルになる。例えばfetchにキャッシュ機構を入れた場合に、asyncだとそのまま返すだけでインタフェースは非同期のまま

ちなみにasync functinoからPromiseを返しても別のPromiseにwrapされることはないので安心

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目28: クラスとカリー化を使用して新しい推論サイトを作成する

type Some = {};

type SomeAPI = {
    '/hoge': Some
}

declare function fetchAPI<API, Path extends keyof API>(path: Path): Promise<API[Path]>;

const result = fetchAPI<SomeAPI, '/hoge'>('/hoge')

これは冗長すぎる。カリー化することで簡潔にかける

type Some = {};

type SomeAPI = {
    '/hoge': Some
}

declare function fetchAPI<API>(): <Path extends keyof API>(path: Path) => Promise<API[Path]>;

const someFetcher = fetchAPI<SomeAPI>();
const result = someFetcher('/hoge')
腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目 29: 常に有効な状態を表す型を好む

個別の状態から、一つの状態を導く出すのは難しい。例えば、ページの状態を表す

  • isLoading
  • error

の2つがあったとして、それらを見ながら描画する画面を作るのは難しい。また、それぞれのステータスの更新を忘れてしまうことともある。そのため、 1つの状態を持つほうがよい

type ErrorState = {
    type: 'error',
    message: string;
}

type LoadingState = {
    type: 'loading'
}

type SuccessState = {
    type: 'success',
    content: 'content'
}

type State = ErrorState | LoadingState | SuccessState;
腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目30:受け入れるものは自由に、生み出すものは厳格に

Optionalな引数を受け取らざるを得ないケースがあったとしても、戻り地は明確な値にすべき

type SomeType = {
  a: string;
  b: number
};

type SomeTypeOptions = Partial<SomeType>;

declare function setOption(opt: SomeTypeOptions): SomeType;

単に反復処理をしたい場合はIterableを受け取る

ArrayLikegenerator を処理することができるため。

declare function sum(values: Iterable<number>): number;

内部の処理では for-of loop を使えばよい

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目31: ドキュメントでタイプ情報を 繰り返さない

型を見ればわかることはコメントに残さない。

例えば、破壊的変更をしないソート関数を実装する場合、その旨をコメントに残すよりreadonlyの値を受け取った方が良い。もし仮に破壊的な変更を内部でしてしまってもエラーで気づける。

腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目2:使用しているTypeScriptオプションを知る

  • noImplicitAnyはJSからの移行などを除きONにすべき
  • noImplicitAnyとstrictNullChecksを導入するとしたら先にnoImplicitAny
腹筋ローラーの力を信じろ腹筋ローラーの力を信じろ

項目3:コード生成は型に依存しないこと を理解する

実行時にTypeScriptの型をチェックし、処理を分岐することはできない。コレを回避するには

  • プロパティをチェックする
  • タグ付きユニオンを用いる
  • クラスを用いる

などの方法がある


TypeScriptはエラーが有ってもコンパイルすることができる


TSではオーバーロードを実際のコードで記述することはできないが、型としては定義できる。
実装は一つしかできない。