kqito's 技術ブログ

技術やプログラミングについて思ったことを呟くブログ

TypeScript Typeメモ

はじめに

最近TypeScriptを使って開発する事が増えました。TypeScript最高!!

その中でよく使用するなと思う事やTypeScript独自の概念についてメモがてらまとめました。(随時更新)

メモ

厳密なオブジェクト定義

redux/fluxなどのactionなどの部分は一つのオブジェクトに対して次々にプロパティが増える可能性があります。そういったオブジェクトのbetterだと思われる定義の仕方を説明します。

例えば次の様なオブジェクトがあるとします

const obj = {
  key1: (): void => {
    // 処理1
  },
  key2: (): void => {
    // 処理2
  },
  key3: (): void => {
    // 処理3
  }
};

このオブジェクトに対して純粋に型付けをするならば

{ [keys: string]: () => void }

となりますが、この場合だと以下の様に型推論が行われません。

obj. // 型推論・補完が行われない

もちろんobjに対して型定義しなければ型推論・補完されます。が、やっぱりこういったオブジェクトに対しても型定義したいです。

また、次々にプロパティが追加されるであろうオブジェクトだった場合、keyの部分を切り離して定義した方がType的に考えていろいろ融通が効きます。

従って、

  • keyの部分をenumで定義
  • objの型定義にenumを用いる

をする事で厳密なオブジェクト定義する事ができます。

enum keys {
  key1,
  key2,
  key3
}

const obj: { [P in keyof typeof keys]: () => void } = {
  key1: (): void => {
    // 処理1
  },
  key2: (): void => {
    // 処理2
  },
  key3: (): void => {
    // 処理3
  }
};

この様にする事で適切に型推論が行われます。

また、それぞれの関数がvoid型ではなく他の型を返すことになってもvalueの部分を別途定義する事でany型を避けて定義する事ができます。

さらには、

keysに対してはkey4を追加したけど、objにはkey4プロパティを追加し忘れる

という状況がobjに対してコンパイルエラーが発生するため未然に防ぐ事ができます。

こっちのほうがTypeScriptの力を発揮できていますね!!

引数/返り値の型を引き抜きたい

以下の様な関数があるとします。

const arrowFunc = (num: number, str: string): void => {
  // 処理
};

この関数の引数や返り値の型が欲しい!ってときに使えるTypeまとめました。

大体はビルドインされてるものが多いんですが...(勉強がてら

引数の型を引き抜きたい

以下の様なTypeを宣言すると引数をunion typeで取得できます。

export type ArgsType<T> = T extends (...args: infer R) => any
  ? R[number]
  : never;

type test = ArgsType<typeof arrowFunc>; // string | number

ただ、一旦踏みとどまって考えて欲しいのがその引数の型を別で定義した方が汎用性や可読性が高くなる可能性があるという事です。 もちろん状況にもよるので、場合に応じて適切に使用するのが好ましいと思いました。

返り値の型を引き抜きたい

以下の様なTypeを宣言すると返り値の型を取得できます。

export type ReturnType<T> = T extends (...args: any[]) => infer T ? T : never;

type test = ReturnType<typeof arrowFunc>; // void

inferすごい便利です。

Undefinedを取り除く

以下のtypeがあるとします。

type Test = {
  a: string | undefined;
  b?: string;
};

これのtypeからundefinedを取り除きたい場合、以下の様なtypeではそれができません。

type Test1 = { [P in keyof Test]: NonNullable<Test[P]> };
// type Test1 = {
//   a: string;
//   b?: string;
// };

これではbが期待するtypeではありません。従ってkey-?を加える事で?を取り除けます。

type Test1 = { [P in keyof Test]-?: NonNullable<Test[P]> };
// type Test1 = {
//   a: string;
//   b: string;
// };

まとめ

TypeScriptを勉強していてMapped TypesやConditional Types便利すぎると感じる事が本当に多いです。が、これらが無かったTypeScriptの時代って結構大変だったんじゃないか説が僕の中で最近トレンドになっていますw

今後もどんどんメモを増やしていきたいと思います。

以上です。