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では一緒に働いてくれる仲間を募集中です!
ご興味がある方は以下リンクよりご確認ください。