【TypeScript】ReturnTypeの知っておきたい12選の使い方

TypeScriptのReturnTypeを活用する方法のサンプルコードと詳細解説TypeScript
この記事は約25分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptは、大規模なプロジェクトを手助けする強力な静的型付けのスーパーセットとして知られています。

型を持つことで、プログラムの予期しないエラーを早期に発見し、コードのリファクタリングを効率的に行うことができます。

その中でも、ReturnTypeという型は関数の返り値の型を効果的に取得するための強力なツールとして知られています。

この記事では、ReturnTypeの基本から応用、注意点、カスタマイズ方法まで、サンプルコードを交えながら徹底的に解説していきます。

●ReturnTypeとは

ReturnTypeはTypeScriptのユーティリティ型の一つで、関数の返り値の型を取得するのに使われます。

たとえば、ある関数が数値を返す場合、ReturnTypeを使ってその関数の返り値の型を取得することができます。

具体的なサンプルコードを見てみましょう。

function sampleFunction(): number {
    return 123;
}

type ResultType = ReturnType<typeof sampleFunction>;

このコードでは、sampleFunctionという関数が数値を返しています。

ReturnType<typeof sampleFunction>という記述を使って、その関数の返り値の型を取得し、ResultTypeという型に代入しています。

結果として、ResultTypenumber型となります。

○ReturnTypeの基本

ReturnTypeの使用は非常にシンプルです。

関数の返り値の型を知りたい場合、ReturnTypeとtypeofキーワードを組み合わせて使用します。

先ほどのサンプルコードでは、typeof sampleFunctionで関数の型を取得し、その後にReturnTypeを使って関数の返り値の型を取得しています。

別の例を見てみましょう。

function greet(): string {
    return "Hello, TypeScript!";
}

type GreetingType = ReturnType<typeof greet>;

上記のコードでは、greet関数が文字列を返しています。

その返り値の型をReturnTypeを使って取得し、GreetingTypeという型に代入しています。

結果として、GreetingTypestring型となります。

ReturnTypeは、関数だけでなく、メソッドやアロー関数でも同様に使用することができます。

プログラミング初心者の方でも、この基本的な使い方を覚えることで、TypeScriptの型システムをより効果的に活用することができるでしょう。

●ReturnTypeの使い方

TypeScriptはJavaScriptのスーパーセットとして、静的型付けの機能を提供しています。

型安全を求めるプログラム開発において、TypeScriptは非常に有用です。

そして、型関連の便利なユーティリティ型も多数用意されています。

その中でも「ReturnType」は、関数の返り値の型を取得するためのユーティリティ型です。

この記事では、ReturnTypeの基本的な使い方とその詳細なサンプルコードを提供します。

初心者から中級者、プログラミング初心者の方でも理解できるように、具体的なコード例とその説明を通して、ReturnTypeの使い方を徹底解説します。

○サンプルコード1:基本的なReturnTypeの利用

まず、ReturnTypeの基本的な使い方を見ていきましょう。

下記のコードは、簡単な関数とその関数の返り値の型をReturnTypeを使って取得する例です。

function sampleFunction() {
  return {
    name: "Taro",
    age: 30
  };
}

type ResultType = ReturnType<typeof sampleFunction>;

このコードでは、sampleFunctionという関数が定義されています。

この関数は、nameとageのプロパティを持つオブジェクトを返します。

次に、ReturnTypeを使用して、sampleFunctionの返り値の型を取得しています。

その結果、ResultTypeという型は、sampleFunctionが返すオブジェクトの型と同じになります。

具体的には、{ name: string; age: number; }という型になります。

このようにReturnTypeは、関数の返り値の型を簡単に取得することができます。

特に大規模なプロジェクトや複雑なコードを扱う際に、ReturnTypeを活用することで、型の再定義や手動での型の取得を避けることができ、効率的な開発が期待できます。

上記のコードを実際にTypeScriptで実行すると、ResultTypesampleFunctionが返すオブジェクトの型、つまり{ name: string; age: number; }という型になります。

これにより、他の関数や変数の型として使用することができるようになります。

○サンプルコード2:オブジェクトのメソッドの返り値の型を取得

TypeScriptでは、型安全性を高めるために多くの便利な型ユーティリティが提供されています。

その中でも、特定の関数やメソッドの返り値の型を取得する際に使用されるReturnTypeは、非常に実用的なものの1つです。

今回は、オブジェクトのメソッドの返り値の型を取得する方法を詳しく解説します。

まず、次のようなオブジェクトを考えてみましょう。

const user = {
    id: 1,
    name: "Taro",
    getInfo() {
        return {
            id: this.id,
            name: this.name
        };
    }
};

このオブジェクトには、getInfoというメソッドがあります。

このメソッドの返り値の型をReturnTypeを使って取得したいと考えた場合、次のように記述します。

type UserInfoType = ReturnType<typeof user.getInfo>;

このコードでは、ReturnTypeを使ってuser.getInfoの返り値の型を取得して、それをUserInfoTypeという型として定義しています。

では、実際にこのコードを実行すると、UserInfoTypeはどのような型になるのでしょうか。

答えは、{ id: number; name: string; }となります。

このように、ReturnTypeを活用することで、関数やメソッドの返り値の型を簡単に取得できるのです。

○サンプルコード3:ネストされたオブジェクトの型を取得

TypeScriptを使ってプログラミングを行う際、ネストされたオブジェクトの型を取得したいケースがしばしば出てきます。

特に大規模なプロジェクトや複雑なデータ構造を持つアプリケーションを開発する際には、この技術は非常に役立ちます。

ReturnTypeを使うことで、これを簡単に実現することができます。

下記のサンプルコードでは、ネストされたオブジェクトの中の関数の返り値の型を取得しています。

// ネストされたオブジェクトのサンプル
const nestedObject = {
  level1: {
    level2: {
      func: () => {
        return {
          name: 'Taro',
          age: 30
        };
      }
    }
  }
};

// ReturnTypeを使って、ネストされた関数の返り値の型を取得
type NestedFuncReturnType = ReturnType<typeof nestedObject.level1.level2.func>;

このコードでは、nestedObjectという名前のオブジェクトの中に、さらにネストされたオブジェクトと関数が定義されています。

そして、ReturnTypeを用いて、nestedObject.level1.level2.funcの関数の返り値の型を取得しています。

このように、ReturnTypeは非常に深いネストの中にある関数の返り値の型も簡単に取得することが可能です。

このコードを実行すると、NestedFuncReturnTypeという型は、{ name: string; age: number; }として取得されることになります。

つまり、関数funcが返すオブジェクトの型を正確に取得することができるのです。

○サンプルコード4:非同期関数の返り値の型を取得

TypeScriptを使用すると、関数の返り値の型を簡単に取得することができます。

特に非同期関数、すなわちPromiseを返す関数の返り値の型の取得は、ReturnTypeと組み合わせることで簡潔に記述することが可能です。

実際のサンプルコードを見てみましょう。

async function fetchData(): Promise<number> {
    return 10;
}

type FetchedData = ReturnType<typeof fetchData>;

このコードでは、非同期関数fetchDataを定義しています。

この関数はPromise<number>型を返します。そして、ReturnTypeを使って、fetchDataの返り値の型をFetchedDataという新しい型として取得しています。

しかし、このままではFetchedDataPromise<number>となり、非同期の中身であるnumber型を直接取得することができません。

非同期関数の返り値の型、すなわちPromiseの中身の型を取得するには、もう少し工夫が必要です。

下記のサンプルコードでは、非同期関数の返り値の型を直接取得する方法を表しています。

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

async function fetchData(): Promise<number> {
    return 10;
}

type FetchedData = UnwrapPromise<ReturnType<typeof fetchData>>;

このコードでは、最初にUnwrapPromiseという型を定義しています。

この型は、渡される型がPromiseである場合、Promiseの中身の型を返す役割を持っています。

そして、このUnwrapPromiseをReturnTypeと組み合わせて、非同期関数fetchDataの返り値の型をFetchedDataとして取得しています。

このコードを実行すると、FetchedDataの型はnumberとなります。

これにより、非同期関数の中身の型を簡単に取得することができるようになりました。

●ReturnTypeの応用例

TypeScriptのReturnTypeは、関数やメソッドの返り値の型を取得するための便利なユーティリティ型として知られています。

しかし、基本的な使い方だけでなく、さまざまな応用例も存在します。

ここでは、ReturnTypeを使ったいくつかの応用例について、サンプルコードと詳細な説明を交えて解説していきます。

○サンプルコード5:複数の関数の返り値を組み合わせる

考え方として、複数の関数があったとき、それぞれの返り値の型を組み合わせて新しい型を作成することが考えられます。

ReturnTypeを用いることで、これを実現することができます。

例えば、次のような2つの関数があるとします。

function getName() {
  return "Taro";
}

function getAge() {
  return 25;
}

この2つの関数の返り値の型を、ReturnTypeを使用して組み合わせてみましょう。

type CombinedType = {
  name: ReturnType<typeof getName>;
  age: ReturnType<typeof getAge>;
};

このコードでは、getName関数の返り値の型をReturnType<typeof getName>を使って取得し、nameのプロパティの型としています。

同様に、getAge関数の返り値の型をReturnType<typeof getAge>を使って取得し、ageのプロパティの型としています。

このコードを実行すると、CombinedTypeは次のような型として認識されます。

{
  name: string;
  age: number;
}

このようにして、複数の関数の返り値の型を組み合わせて新しい型を作成することができます。

結果的に、CombinedTypeはnameageという2つのプロパティを持ったオブジェクトの型となります。

それぞれのプロパティの型は、元となった関数の返り値の型に基づいています。

この方法を利用することで、既存の関数の返り値の型を再利用して、新しい型を効率的に作成することができるのです。

○サンプルコード6:条件付きの返り値の型を取得

TypeScriptには、関数の返り値の型を効率的に取得するためのReturnTypeという組み込み型が存在します。

これを利用すると、関数が返す型を容易に推定できます。

今回は、そのReturnTypeを使用して、条件付きの返り値の型を取得する方法を詳しく説明します。

まず、条件付きの関数とは、条件に応じて異なる型の値を返す関数のことを指します。

例えば、次のような関数を考えてみましょう。

function conditionalReturn(flag: boolean): number | string {
    if (flag) {
        return "こんにちは、TypeScript!";
    } else {
        return 12345;
    }
}

この関数は、引数flagtrueの場合は文字列を、falseの場合は数値を返します。

この関数の返り値の型をReturnTypeを使って取得すると、次のようになります。

type ConditionalReturnType = ReturnType<typeof conditionalReturn>;

このコードでは、ReturnTypeを使って、conditionalReturn関数の返り値の型を取得しています。

結果として、ConditionalReturnTypenumber | stringという型を持つことになります。

このコードを実行すると、条件に応じて異なる型の値を返す関数の返り値の型を正確に取得することができます。

○サンプルコード7:マッピング型との組み合わせ

TypeScriptでは、マッピング型を使用して、既存の型を新しい型に変換することができます。

これは非常に強力な機能であり、ReturnTypeと組み合わせることで、さらに高度な型操作を行うことができます。

マッピング型は、キーを取り、そのキーに関連付けられた新しい型を生成するための型です。

TypeScriptには、このような高度な型操作をサポートする機能が含まれています。

それでは、具体的なサンプルコードを見てみましょう。

type User = {
  name: string;
  age: number;
  getAddress: () => {
    city: string;
    state: string;
  };
};

type FunctionProperties<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
};

type FunctionReturnTypes<T> = {
  [K in FunctionProperties<T>]: ReturnType<T[K]>;
};

type UserFunctionReturnTypes = FunctionReturnTypes<User>;

このコードでは、まずUserという型を定義しています。

そして、マッピング型FunctionPropertiesを使用して、User型内の関数プロパティのみを取得しています。

最後に、FunctionReturnTypesマッピング型を使用して、それらの関数の返り値の型を取得しています。

具体的には、FunctionProperties<User>の結果はgetAddressという関数名のみを持っている型になります。

そして、FunctionReturnTypes<User>の結果は、getAddressが返す型({ city: string; state: string; })を持っている型になります。

この結果、UserFunctionReturnTypesの型は次のようになります。

{
  getAddress: {
    city: string;
    state: string;
  };
}

これにより、特定のオブジェクトの中で定義されている関数の返り値の型を簡単に取得できます。

これは、大規模なプロジェクトやライブラリの型定義時に非常に役立ちます。

○サンプルコード8:ジェネリックとの組み合わせ

TypeScriptでは、ジェネリックという非常に強力な機能を提供しています。

ジェネリックは、型の再利用や汎用的な関数やクラスを作成する際に使用されます。

ReturnTypeとジェネリックを組み合わせることで、さらに柔軟で再利用しやすい型を作成することが可能となります。

まず、ジェネリックとは何かを簡単に解説します。

ジェネリックは、型を引数として取ることができる仕組みです。

関数やクラスを定義する際に、具体的な型を固定せずに、後から型を指定することができます。

これにより、汎用的な関数やクラスを作成することができます。

例として、ジェネリックを使用した関数を考えてみましょう。

function identity<T>(arg: T): T {
    return arg;
}

このコードでは、identity関数はジェネリックを使っています。

<T>という部分がジェネリックの型引数です。

この関数は、どんな型の値でも受け取ってそのまま返すことができます。

さて、このジェネリック関数を使ったReturnTypeの例を考えてみましょう。

type ResultType<T> = ReturnType<typeof identity<T>>;

このコードでは、ジェネリック関数identityの返り値の型をResultTypeとして定義しています。

ここでのReturnTypeはジェネリック関数を受け取ることができ、その返り値の型を取得します。

このコードを実行すると、ResultTypeはジェネリック型として定義されるため、次のような使用が可能となります。

let num: ResultType<number> = identity(123);
let str: ResultType<string> = identity("abc");

最初にidentity関数で数値123を渡すと、ResultType<number>は数値型となります。

同様に、文字列"abc"を渡すと、ResultType<string>は文字列型となります。

●注意点と対処法

TypeScriptのReturnTypeは非常に便利で、多くの場面で型の取得を助けてくれます。

しかし、使い方によっては、予期せぬエラーや問題が発生することも。

そのため、注意点を理解し、適切な対処法を理解することが大切です。

一般的な注意点としては、ReturnTypeは関数の返り値の型を取得するものであり、それ以外の値やオブジェクトに対して使用すると予期しない結果を返すことがあります。

また、ジェネリックな関数やオーバーロードされた関数に対してReturnTypeを適用する場合、複数の型が考慮されるため、正確な返り値の型を取得するのが難しくなることがあります。

○サンプルコード9:ReturnTypeが期待する型を返さない場合の対処法

下記のサンプルコードは、ReturnTypeが期待する型を返さない場合の例を表しています。

type User = {
    name: string;
    age: number;
};

function getUser(): User {
    return {
        name: 'Taro',
        age: 25,
    };
}

// ReturnTypeを使って関数getUserの返り値の型を取得
type ExtractedUser = ReturnType<typeof getUser>;

// User型を期待しているが、以下のように異なるプロパティを持つオブジェクトを返すと、コンパイルエラーが発生する
function getModifiedUser(): ExtractedUser {
    return {
        name: 'Jiro',
        hobby: 'Reading', // Error! 'hobby' does not exist in type 'ExtractedUser'
    };
}

このコードでは、getModifiedUser関数がExtractedUser型を返すことを期待しています。

しかし、getModifiedUser関数内でhobbyというプロパティを持つオブジェクトを返しているため、コンパイルエラーが発生します。

対処法としては、関数の返り値の型を明示的に定義するか、またはReturnTypeを使用する場面を適切に選ぶことが必要です。

また、関数の返り値の型が変更された場合、その変更を受け取る関数や変数の型も適切に更新することで、このような問題を避けることができます。

このコードを実行すると、上記の通りコンパイルエラーが発生します。

これは、getModifiedUser関数の返り値がExtractedUser型と一致しないためです。

実際のプログラミング初心者はこのようなエラーに直面することが多く、ReturnTypeの適切な使い方を理解することで、よりスムーズにTypeScriptのプログラミングができるようになります。

○サンプルコード10:非同期関数の型の取得時の注意点

TypeScriptを使い始めると、非同期関数の返り値の型を取得する場面に出くわすことが増えます。

ReturnTypeを使用して非同期関数の返り値の型を取得する際には注意が必要です。

非同期関数は通常、Promiseを返すため、ReturnTypeを直接使用すると、Promiseの型が取得されてしまいます。

例として、次の非同期関数を考えてみましょう。

async function fetchData(): Promise<string> {
  // 何らかのデータを取得する処理
  return "sample data";
}

この関数は文字列を返すPromiseを返します。

ReturnTypeを使ってこの関数の返り値の型を取得すると、以下のようになります。

type ResultType = ReturnType<typeof fetchData>;
// ResultTypeはPromise<string>となる

このコードでは、fetchData関数を使って返り値の型を取得しています。

しかし、実際に取得される型はPromise<string>となります。

非同期関数の返り値の型を取得したい場合は、このようにReturnTypeを使うだけでは期待する型を取得することができません。

非同期関数の返り値の型を正しく取得するためには、ジェネリックを使用してReturnTypeを拡張する必要があります。

下記の方法で非同期関数の返り値の型を正しく取得することができます。

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

// 上記のAsyncReturnTypeを使用して非同期関数の返り値の型を取得
type CorrectResultType = AsyncReturnType<typeof fetchData>;
// CorrectResultTypeはstringとなる

このコードでは、AsyncReturnTypeという新しい型を定義しています。

この型は非同期関数を引数として受け取り、その関数の返り値の型を取得する役割を果たします。

fetchData関数の返り値の型をAsyncReturnTypeを使って取得すると、stringという期待する型を取得することができます。

●カスタマイズ方法

ReturnTypeはTypeScriptの便利なユーティリティ型の1つですが、プロジェクトに合わせてReturnTypeをカスタマイズする方法もあります。

ここでは、独自のReturnTypeの定義方法を詳細に解説します。

○サンプルコード11:独自のReturnTypeを定義する

ReturnTypeをより具体的なシチュエーションに適応させるためには、ジェネリックを活用したカスタマイズが考えられます。

独自のReturnTypeを定義するサンプルコードを紹介します。

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

// 使用例
function sampleFunction(): { name: string; age: number } {
  return { name: "Taro", age: 25 };
}

type SampleType = MyReturnType<typeof sampleFunction>;

このコードでは、MyReturnTypeという名前の新しい型を定義しています。

この型は、ReturnTypeと同じように、関数の返り値の型を取得します。

しかし、独自に名前をつけていますので、ReturnTypeとは異なる名前で型を定義することができます。

このコードを実行すると、SampleTypesampleFunction関数の返り値、すなわち{ name: string; age: number }という型を持つことになります。

これにより、MyReturnTypeを使用して、独自のReturnTypeを定義することができます。

注意していただきたいのは、このカスタマイズ方法はReturnTypeの動作を変更するものではなく、ReturnTypeと同じ動作を持つ新しい型を作成するものです。

プロジェクト内でReturnTypeとは異なる名前で返り値の型を取得する型を使用したい場合に役立ちます。

カスタマイズの利点として、独自の型を作成することで、チームの他のメンバーやプロジェクトの他の部分との型名の衝突を避けることができます。

特に大規模なプロジェクトや、多くのライブラリや外部のモジュールを使用する場合には、型の名前が衝突する可能性があります。

そのような場合に、このようなカスタマイズ方法が役立ちます。

○サンプルコード12:複雑なオブジェクトの型をカスタマイズする方法

TypeScriptを使用しているプログラミング初心者から中級者まで、より柔軟な型のカスタマイズを行いたい場合が多々あります。

特に、大規模なプロジェクトや複雑なオブジェクト構造を持つ場合、ReturnTypeを使って効果的に型を抽出し、それをさらにカスタマイズする方法は非常に役立ちます。

今回は、ReturnTypeを使用して複雑なオブジェクトの型をカスタマイズするサンプルコードを解説します。

これをマスターすれば、TypeScriptの型システムをより深く、そして効果的に活用することができるようになります。

// 複雑なオブジェクトのサンプル
type ComplexObject = {
    user: {
        id: number;
        profile: {
            name: string;
            age: number;
            address: {
                city: string;
                zipcode: string;
            }
        }
    }
}

// ReturnTypeを使って、profileの型を抽出する関数を定義
type GetUserProfile = (obj: ComplexObject) => typeof obj.user.profile;
type UserProfile = ReturnType<GetUserProfile>;

このコードでは、まず複雑なネストされたオブジェクトComplexObjectを定義しています。

このオブジェクトはユーザー情報やプロフィール、住所などの情報を持っています。

次に、ReturnTypeを使用してComplexObjectからprofileの型を抽出する関数GetUserProfileを定義します。

この関数はComplexObjectを受け取り、profileの部分を返すことを期待しています。

そして、ReturnTypeを使ってこの関数の返り値の型をUserProfileとして抽出しています。

このコードを実行すると、UserProfileは次のような型として定義されます。

type UserProfile = {
    name: string;
    age: number;
    address: {
        city: string;
        zipcode: string;
    }
}

このように、ReturnTypeを使うことで複雑なオブジェクトから特定の部分の型を簡単に抽出し、それを別の型として再利用することができます。

この方法は、大規模なプロジェクトや複数の型を共有するモジュールを持つ場合などに非常に役立ちます。

しかし、注意する点として、ReturnTypeは関数の返り値の型を取得するものであるため、正しく関数を定義することが重要です。

関数の中で意図しない型を返してしまうと、その後の型の利用で予期しないエラーが発生する可能性があります。

関数を使用する場面では、常に関数の定義とその返り値をチェックし、正しい型が返されることを確認するようにしましょう。

また、カスタマイズの際にはジェネリックやマッピング型など、TypeScriptの高度な型システムを組み合わせることで、さらに柔軟な型定義が可能です。

初心者の方は、まず基本的な使い方からスタートし、慣れてきたら徐々に応用的な使い方に挑戦すると良いでしょう。

まとめ

TypeScriptの強力な型機能の1つとして「ReturnType」が存在します。

ReturnTypeは、関数の返り値の型を取得するためのユーティリティ型であり、多岐にわたる場面で有用です。

今回の記事では、ReturnTypeの基本的な使い方から応用例、注意点、さらにカスタマイズ方法まで、一通りの内容をサンプルコードを交えて解説しました。

プログラミング初心者から中級者まで、多くの方がTypeScriptの型システムを効果的に活用する際の参考として、この記事が役立てば幸いです。