頑張らないために頑張る

ゆるく頑張ります

ReturnTypeを使って関数の戻り値の型を生成する方法

Posted at — Oct 3, 2023

概要

ライブラリ内の関数を利用する場合において、その関数の戻り値がオブジェクトであるとき、自分でtypeinterfaceを記述しなくても、関数の戻り値を参照して自動的に型情報を生成してくれる機能があります。それがUtility Typesの一機能でReturnTypeというもの。

ReturnTypeはTypeScriptのユーティリティ型で、指定した関数の戻り値の型を確定する機能を提供します。これにより、関数の戻り値の型をプログラマーが明示的に指定することなく、すでにある関数の仕様から抽出して戻り値の型を使用できます。これにより、型の安全性を提供し、バグの防止と修正しやすさを支援することで、コード品質の向上が期待できます。

メリット

サンプルコード

ReturnTypeを使ったサンプルコードを考えます。

type Hoge ={
    foo: string;
    bar: number;
    baz: Array<string | number>;
}

function hoge(): Hoge{
    return {
        foo: 'eggs',
        bar: 1,
        baz: [2, 'ham', 3, 4, 'spam']
    };
}

type HogeReturnType = ReturnType<typeof hoge>

let resultOfHoge: Hoge = hoge();

let resultOfHoge2: HogeReturnType = hoge();

型情報のHogeは自分で記述したものです。対して、関数hogeの戻り値をtypeofで調べ、結果をReturnTypeを用いて格納したのがHogeReturnTypeです。こうすることで、型情報を記述する手間を省き、誤った内容を記述したり誤った値が格納されるリスクを軽減しています。

使いどころ

複雑なオブジェクトを扱う場合

プロパティが多かったり複雑なオブジェクトを返す関数がある場合、ReturnTypeを使用することで戻り値の型を明確に定義し強制することが可能です。

function calc(a: number, b: number) {
  return {
    add: a + b,
    sub: a - b,
    mul: a * b,
    div: a / b,
    mod: a % b,
    formula: 'Calculation result using the values of ' + a.toString() + ' and ' + b.toString()
    }
}

type calcReturnType = ReturnType<typeof calc>;
const result: calcReturnType = calc(10, 4);

console.log(result);

上記のサンプルコードにおいて、関数calcは2つの数値の計算結果と文章を返す関数です。ReturnTypeを使用することで、戻り値の型の内容をプログラマーが記述することなく使用できます。

とは言え、「そもそも、プロパティが複雑で数も多いようなオブジェクトを作るんじゃない!」っていうのはまさにその通りなので、上記の例で言うならまず対応すべきはオブジェクトのシンプル化だとは思います。ワイトもそう思います

サードパーティのライブラリやAPIを扱う場合

外部のライブラリやAPIを扱う場合、関数の戻り値の型が必ずしも明確でないことがあります。そのような場合、ReturnTypeを使用することで、ライブラリが提供する戻り値の型を抽出して利用することが可能です。

import axios from 'axios';

async function fetchData(): Promise<any> {
  const response = await axios.get('https://example.com/posts');
  return response.data;
}

type FetchDataReturnType = ReturnType<typeof fetchData>;

async function processData(): Promise<void> {
  const data: FetchDataReturnType = await fetchData();
  console.log(data);
}

この例において、関数fetchDataはAxiosを使用してデータを取得しあmす。fetchDataの実際の戻り値の型は、 ReturnTypeを使用して取得します。これにより、関数を使用する際にデータに沿った型で宣言することが可能です。

既存のコードをリファクタリングまたは修正する場合

ReturnTypeは、戻り値の型が変更される(可能性のある)既存のコードをリファクタリング、または修正する際に役立ちます。

// ここの関数を変更中!
function getUser(): { name: string; age: number } {
  return {
    name: 'John',
    age: 30,
  };
}

type GetUserReturnType = ReturnType<typeof getUser>;

const user: GetUserReturnType = getUser();

この例では、関数getUserは特定の構造を持つオブジェクトを返します。その際、何らかの形で返されるオブジェクトの構造が変更された場合でも、ReturnTypeは自動的に型情報を実行ごとに取得するため、変更後の情報を取得できます。

余談

ReturnTypeで得た結果の型情報を出力することはどうもできないらしく、TypeScriptのPlaygroundで試してみたらエラーになりました。

‘HogeReturnType’ only refers to a type, but is being used as a value here.

「’HogeReturnType’は型情報であって値そのものではないっすよ。なのに値として扱われている(表示しようとしている)っすよ」というエラーです。ただ、とりあえず型をちゃんと取得できていれば、オブジェクトの構造はIDE上でサジェストされるはずなのでそんなに気にならないはずだとは思います。ワイトもそう思います

まとめ

ReturnTypeはTypeScriptの便利なユーティリティ型で、関数の戻り値の型を抽出して利用できます。ReturnTypeは型の安全性を提供し、コードの可読性を向上させ、リファクタリングを支援し、エラーの検出と修正を支援することでコードの品質を向上させてくれます。

ReturnTypeを有効に活用することで、より信頼性が高く保守性の高いコードを書くことができそうです。

reference

  1. Utility Types
  2. Object Types
  3. 【TypeScript】Utility TypesのReturnTypeを深く理解する
  4. 【TypeScript】型定義で関数の戻り値を直接使えるReturnType
comments powered by Disqus