LoginSignup
2

More than 1 year has passed since last update.

【TypeScript】inferは怖くない

Last updated at Posted at 2022-12-15

TypeScriptの学習を進めていく中で、多分ぶつかるであろう壁がinferではないでしょうか。
何をしているのかわからず、普段使用しないから無視している方もいらっしゃるかと思います。
しかし、inferはよく使用されています。知らないところで型を推論してくれています。

今回は例を交えながらinferの特徴と使用方法について解説していきます。

何がinferの理解を妨げているのか

まずはConditional Typesの理解不足からくるものが考えられます。
以下公式からじっくりみていくことをおすすめします。
https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#handbook-content

また、inferという単語から複雑さを感じやすいのも一つです。
いきなり多用されている部分をみると理解しづらい状況になってしまいます。

ですが、一つ一つを順番に読んでいけば難しいものではなく、むしろ役に立ちそうな便利な型推論になります。

infer とは

inferとは、条件付き型のextends内で、推論される型を導入するための宣言です。
条件付きとは、Conditional Typesのことです。

Conditional Typesについて詳細な説明は省きますが、以下のような形をとります。
T型がU型に含まれている時に、X型を返し、含まれなければY型を返します。

T extends U ? X : Y

環境

今回試した環境は、公式TypeScript TS Playgroundです。
https://www.typescriptlang.org/play

バージョンは v4.9.4 になります。

ReturnType

Utility Typesの中にReturnTypeがあります。これは指定した関数の返り値の型を推論してくれる便利な型です。
実はここでinferが使用されています。
https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype

MyReturnTypeは、関数であるならばその関数の返り値をinferを使用して推論し型として定義しています。

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

const sum = (a: number, b: number) => a + b
type Sum = MyReturnType<typeof sum> // number

実際にReduxのアクション型を定義するのに一役買ってくれています。
アクションの関数返り値をまとめて定義でき、reducerで使用しています。

const INCREMENT = 'INCREMENT' as const;
const SET_COUNT = 'SET_COUNT' as const;

export const increment = () => ({
  type: INCREMENT,
})

export const setCount = (num: number) => ({
  type: SET_COUNT,
  payload: {
    count: num,
  },
})

type Actions = (
  | ReturnType<typeof increment>
  | ReturnType<typeof setCount>
)
/*
type Actions = {
    type: "INCREMENT";
} | {
    type: "SET_COUNT";
    payload: {
        count: number;
    };
}
*/

export default function reducer(
  state: any,
  action: Actions,
) {
  switch (action.type) {
    // 省略
  }
}

Flatten型

以下から拾ってきました。
https://www.typescriptlang.org/docs/handbook/2/conditional-types.html

Typeが配列ならばその中身の型をinferで推測し、型として定義しています。

type Flatten<T> = T extends Array<infer U> ? U : T;

const users = [
  {
    name: 'a',
    age: 25
  },
  {
    name: 'b',
    age: 30
  }
]
type MyObject = Flatten<typeof users>
/*
type MyObject = {
    name: string;
    age: number;
}
*/

Unpack型

以下から拾ってきました。
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types

配列要素型、関数戻り型、promise.resolve引数型を取得する型になります。
難しいように見えますが、Conditional Typesが多いだけです。

type Unpacked<T> =
 T extends (infer U)[] ? U :
 T extends (...args: any[]) => infer U ? U :
 T extends Promise<infer U> ? U
 T

GoQSystemでは一緒に働いてくれる仲間を募集中です!

ご興味がある方は以下リンクよりご確認ください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2