読み込み中...

TypeScriptのPickを完全解説!10選のサンプルコードで習得!

TypeScriptのPick関数のイラストとサンプルコードを表す図解 TypeScript
この記事は約23分で読めます。

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

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

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

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

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

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

はじめに

TypeScriptは、JavaScriptに静的な型を追加することで、大規模なプロジェクトやチームワークをサポートするためのスーパーセット言語として広く採用されています。

TypeScriptの中には、多くのユーティリティ型が提供されており、それらを適切に使用することで、コードの品質を向上させることができます。

今回の記事では、TypeScriptのユーティリティ型の中でも特に便利な「Pick」に焦点を当てて、その仕組みや使い方を徹底的に解説します。

初心者の方でも理解しやすいよう、詳細なサンプルコードを交えて説明を進めていきます。

この記事を通じて、TypeScriptのPickを効果的に使用する技術を習得し、プログラムの品質を一層高める手助けとしていただければ幸いです。

●TypeScriptのPickとは

TypeScriptのPickは、ある型から特定のプロパティだけを取り出して、新しい型を作成するユーティリティ型です。

具体的には、オブジェクト型の一部のプロパティを選択して、その部分だけの型を作り出すことができます。

このような機能は、大きなオブジェクト型から必要な部分だけを抜き出して新しい型を定義する際に非常に役立ちます。

例えば、次のようなUser型があるとします。

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

このUser型から、idnameだけを持つ新しい型を作りたい場合、Pickを使用して次のように記述できます。

type PickedUser = Pick<User, 'id' | 'name'>;

このコードでは、User型からidnameのプロパティだけを持つPickedUser型が定義されます。

このように、Pickは非常にシンプルな構文で、必要なプロパティだけを持つ新しい型を簡単に作成することができるのが大きな特徴です。

また、この機能を使用することで、コードの冗長性を減少させることができ、また、型の変更や拡張が容易になります。

特に大規模なプロジェクトや複数の開発者と協力して開発を進める場合、Pickを適切に利用することで、コードのメンテナンス性を向上させることが期待できます。

○Pickの基本的な説明

Pickは、第一引数に対象となる型、第二引数に抽出したいプロパティのキーを受け取ります。

そして、その結果として、対象の型から指定されたプロパティだけを持つ新しい型を返します。

基本的な形は次のようになります。

type PickedType = Pick<OriginalType, 'property1' | 'property2'>;

上記の例では、OriginalTypeからproperty1property2だけを持つPickedTypeが作成されます。

この機能は、大規模なデータ構造から特定の情報だけを取り出す必要がある場合や、APIのレスポンス型をカスタマイズしたい場合など、様々なシチュエーションで活用できます。

特に、型の再利用性を高めたい場合や、コードの冗長性を排除したい場合には、Pickの使用は非常に有効です。

●Pickの使い方

TypeScriptは、JavaScriptの上に型システムを持つ強力なスクリプト言語です。

この言語の特徴的な機能の1つがユーティリティ型であり、その中でも特に「Pick」は非常に便利です。

ここでは、Pickの基本的な使い方をサンプルコードを交えて詳しく解説していきます。

○サンプルコード1:基本的なオブジェクトからプロパティを抽出

まず、基本的なオブジェクトから特定のプロパティを抽出する方法を見ていきましょう。

TypeScriptで定義された「Person」オブジェクトの例を紹介します。

type Person = {
    name: string;
    age: number;
    address: string;
};

このオブジェクトから、「name」と「age」だけを抽出したい場合、Pickを使用します。

このコードでは、Person型を使ってnameとageのプロパティだけを持つ新しい型「NameAndAge」を定義しています。

type NameAndAge = Pick<Person, 'name' | 'age'>;

このコードを実行すると、新しい型「NameAndAge」は次のような型定義となります。

{
    name: string;
    age: number;
}

このサンプルコードのポイントは、’name’ | ‘age’ の部分で、抽出したいプロパティ名を|で区切って指定していることです。

これによって、複数のプロパティを指定して新しい型を作成することができます。

○サンプルコード2:ネストされたオブジェクトのプロパティを抽出

TypeScriptでは、オブジェクトの中にさらに別のオブジェクトがネストされている場合、そのネストされたオブジェクトのプロパティを取り出すことができます。

このような複雑なオブジェクトから特定のプロパティを抽出する場合、Pick型は非常に有効です。

こちらがそのサンプルコードとなります。

type UserProfile = {
  id: number;
  name: string;
  address: {
    street: string;
    city: string;
    country: string;
  };
};

type UserAddress = Pick<UserProfile, 'address'>;

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

この型にはaddressというプロパティが存在し、その中にさらにstreet, city, countryというプロパティがネストされています。

次に、Pick<UserProfile, 'address'>を使用して、UserProfileからaddressのみを抽出して、新しい型UserAddressを作成しています。

このコードを実行すると、UserAddress型は次のような形になります。

type UserAddress = {
  address: {
    street: string;
    city: string;
    country: string;
  };
};

このように、Pick型を使うことで、ネストされたオブジェクトの特定のプロパティだけを簡単に取り出して、新しい型を作成することができます。

また、ネストされたオブジェクトの中の特定のプロパティだけを取り出すこともできます。

例えば、addressの中のcityだけを取り出したい場合は、次のように記述します。

type UserCity = Pick<UserProfile, 'address'>['address']['city'];

この方法で、UserCity型はcityの型、すなわちstringとして定義されることになります。

○サンプルコード3:複数のプロパティを同時に抽出

TypeScriptにおけるPick型の魅力的な特性の一つは、複数のプロパティを同時に抽出することができる点です。

これは、大きなオブジェクトから特定のプロパティのみを取り出して新しい型を作成したいときに非常に役立ちます。

ここでは、Pick型を使用して複数のプロパティを同時に抽出する方法を、詳細なサンプルコードを通して解説します。

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

// ユーザープロファイルの型定義
type UserProfile = {
  name: string;
  age: number;
  email: string;
  address: string;
  phoneNumber: string;
};

この型から、nameemailの2つのプロパティだけを抽出して新しい型を作成したいとします。

下記のコードでは、Pickを使って指定した複数のプロパティを抽出しています。

// nameとemailのみを持つ新しい型を定義
type UserNameAndEmail = Pick<UserProfile, 'name' | 'email'>;

このコードを実行すると、UserNameAndEmailは次のような型となります。

{
  name: string;
  email: string;
}

○サンプルコード4:動的なキーを使用してプロパティを抽出

TypeScriptにおけるPickの利用方法を深掘りしていきます。

特に動的なキーを使用した時の挙動について、サンプルコードを交えて詳しく解説します。

JavaScriptやTypeScriptにおいて、オブジェクトのキーは通常文字列やシンボルとして定義されます。

しかし、それらのキーを動的に操作することも可能です。

具体的には、変数をキーとして使用したり、計算されたキーを利用することができます。

動的なキーを使用する主な理由は、プログラムの柔軟性を高めるためです。

例えば、外部からの入力やAPIのレスポンスに基づいてオブジェクトの特定のプロパティを操作したい場合など、キーの名前が予め定まっていない状況で非常に有効です。

動的なキーを使用してPickを適用する例を紹介します。

type User = {
  id: number;
  name: string;
  email: string;
};

function getDynamicProperty<T, K extends keyof T>(key: K): Pick<T, K> {
  return key;
}

const key = "email" as const; // 動的にキーを取得
const result = getDynamicProperty<User, typeof key>(key);

このコードでは、getDynamicProperty関数を定義しています。

この関数は、与えられたキーを元に、指定された型のオブジェクトからそのキーに関連するプロパティのみを抽出する役割を持っています。

そして、keyという変数を用いて動的にキーを取得し、この関数に適用しています。

このコードを実行すると、result{ email: string; }という型を持つことになります。

これにより、User型のemailプロパティだけが取り出されることが確認できます。

●Pickの応用例

TypeScriptでの型操作の中で、Pickは非常に便利なユーティリティ型の一つです。

基本的な使い方をマスターした後、より実用的なシナリオでの応用例を見ていきましょう。

○サンプルコード5:条件に基づいてプロパティを抽出

TypeScriptのPickは、型から特定のプロパティを抽出するためのものですが、ある条件に基づいてプロパティを抽出することも考えられます。

例えば、特定の型が持っているプロパティの中で、特定の文字列を含むものだけを取得するといった応用が考えられます。

type Profile = {
    name: string;
    age: number;
    address: string;
    email: string;
    phone: number;
};

// 'address'や'email'のように'e'を含むプロパティだけを抽出
type ExtractPropertiesWithType<T, U extends string> = {
    [K in keyof T as T[K] extends U ? K : never]: T[K];
};

type Result = ExtractPropertiesWithType<Profile, 'e'>;

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

Profile型の中で、'e'という文字列を型として持つプロパティだけを抽出するという操作を行っています。

このコードを実行すると、Result型はaddressemailというプロパティだけを持った型として評価されます。

○サンプルコード6:関数のパラメーターとしての利用

TypeScriptでは、特定のオブジェクトから必要なプロパティのみを抽出する際に、Pickという組み込みのユーティリティ型を使用します。

このPickは関数のパラメータとしても非常に役立ちます。今回はその具体的な利用方法をサンプルコードを通してご紹介します。

まず、次のようなユーザー情報を保持するオブジェクト型Userを考えてみましょう。

// ユーザー情報のオブジェクト型
type User = {
    id: number;
    name: string;
    age: number;
    address: string;
};

この型の中から、関数の引数として、idnameのみを取りたい場合が考えられます。

ここでPickを利用して関数を定義すると以下のようになります。

// Pickを用いて、idとnameのみを引数とする関数を定義
function getUserInfo(user: Pick<User, 'id' | 'name'>) {
    console.log(`ID: ${user.id}, 名前: ${user.name}`);
}

このコードでは、Pick<User, 'id' | 'name'>を使って、User型の中からidnameプロパティのみを取り出しています。

このようにして定義した関数getUserInfoは、次のように使用することができます。

const user: User = {
    id: 1,
    name: "田中",
    age: 25,
    address: "東京都"
};

// getUserInfo関数を呼び出す
getUserInfo(user); // 結果:ID: 1, 名前: 田中

このコードを実行すると、getUserInfo関数はuserオブジェクトからidnameのプロパティのみを取得して、それを元にログを出力します。

結果として、コンソールにはID: 1, 名前: 田中と表示されます。

○サンプルコード7:外部ライブラリの型との連携

TypeScriptの強力な型システムは、外部ライブラリの型ともシームレスに連携できます。

特に、ライブラリが提供する型を部分的に取得・使用したい場合、Pick型は非常に役立ちます。

外部ライブラリの型を部分的に利用する場合の例として、一般的な日付操作ライブラリであるmoment.jsを取り上げます。

このライブラリにはMomentという型が定義されており、その中の一部のプロパティだけを取得したい場合が考えられます。

例えば、Moment型からformatdiffといったメソッドだけを取得した型を作成する場合、次のようなサンプルコードを考えられます。

import { Moment } from 'moment';

type CustomDateMethods = Pick<Moment, 'format' | 'diff'>;

const dateFunction = (momentObj: CustomDateMethods) => {
    return momentObj.format('YYYY-MM-DD');
}

このコードでは、moment.jsからMoment型をインポートしています。

そして、Pickを使って、Moment型からformatdiffメソッドだけを取得して、新しい型CustomDateMethodsを定義しています。

最後に、この新しい型をパラメータとして受け取る関数dateFunctionを定義しています。

この関数内で、formatメソッドを使用して、日付をYYYY-MM-DD形式でフォーマットして返しています。

このコードを実行すると、momentオブジェクトを引数としてdateFunctionに渡すことで、その日付を指定した形式で取得できます。

○サンプルコード8:共通のプロパティを持つ複数の型の結合

TypeScriptを使用する際の一つのポピュラーなシチュエーションは、いくつかのオブジェクト型があり、これらの型の一部だけを取得して新しい型を作成したい場合です。

これは特に、異なる型に共通のプロパティが存在する時に役立ちます。

今回は、このような状況でPickを使用する方法を表すサンプルコードを取り上げます。

下記のサンプルコードは、2つのオブジェクト型TypeATypeBが存在し、両方の型に共通するnameというプロパティだけを取得して、新しい型CommonPropertyTypeを作成するものです。

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

type TypeB = {
  name: string;
  occupation: string;
};

type CommonPropertyType = Pick<TypeA, 'name'> & Pick<TypeB, 'name'>;

このコードでは、まずTypeATypeBという2つのオブジェクト型を定義しています。

そして、Pickを用いてそれぞれからnameプロパティのみを抽出し、その後、&を使用してこれらを結合しています。

結果として、CommonPropertyTypenameプロパティのみを持つ型となります。

このコードを実行すると、CommonPropertyTypeという新しい型が定義されます。

この型はnameプロパティのみを持つこととなり、そのプロパティの型はstringとなります。

次のようにCommonPropertyTypeを使用してオブジェクトを定義することが可能です。

const person: CommonPropertyType = {
  name: "Taro"
};

しかし、このpersonオブジェクトにageoccupationといったプロパティを追加しようとすると、TypeScriptの型チェックによりエラーが発生します。

もし、2つの型に存在する共通のプロパティだけでなく、それぞれの型に固有のプロパティも取得したい場合は、次のようにして結合することができます。

type CombinedType = TypeA & TypeB;

このコードを実行すると、CombinedTypeTypeATypeBのすべてのプロパティを持つ型として定義されます。

したがって、CombinedTypeのオブジェクトには、name, age, およびoccupationの3つのプロパティが含まれます。

○サンプルコード9:デコレータとの連携

TypeScriptにおいて、デコレータはクラスやメソッド、プロパティ、パラメータに特定の動作や値を追加するための宣言的な方法として利用されます。

一方、Pickは、指定したキーのみを持つ型を作成するユーティリティ型として存在します。

これらの機能を組み合わせれば、デコレータを利用して特定のプロパティを持つオブジェクトの型を動的に生成することも可能です。

まずはデコレータを使って、オブジェクトのプロパティを取得するサンプルをご覧いただきましょう。

// デコレータ関数
function extractProperty(target: any, propertyKey: string) {
    if (!target._properties) {
        target._properties = [];
    }
    target._properties.push(propertyKey);
}

class User {
    @extractProperty
    public name: string;

    @extractProperty
    public age: number;

    public address: string;
}

const userProperties = (new User())._properties;

このコードでは、extractPropertyデコレータを使って、Userクラスのnameageプロパティにマークを付けています。

この結果、userProperties配列にはnameageのキーが含まれます。

次に、上記で取得したプロパティキーを使用して、Pickを使ってサブタイプを作成します。

type ExtractedUser = Pick<User, typeof userProperties[number]>;

このコードでは、User型からuserPropertiesで取得したプロパティのみを持つ新しい型ExtractedUserが生成されます。

このExtractedUser型はnameageプロパティのみを持ち、addressプロパティは含まれません。

このコードを実行すると、ExtractedUser型は次のような型として解釈されることになります。

{
    name: string;
    age: number;
}

この方法を使えば、大規模なプロジェクトやライブラリで、特定のプロパティを持つオブジェクトの型を動的に生成したい場面においても、デコレータとPickを連携させることで効率的にコードを書くことが可能になります。

○サンプルコード10:APIのレスポンス型のカスタマイズ

TypeScriptを利用する開発者として、APIのレスポンス型を柔軟に扱いたいと思うことは多々あります。

特に大規模なプロジェクトや、多数のAPIレスポンス型が存在する場合、必要なプロパティだけを取得したいと思うことは日常茶飯事でしょう。

その際に非常に役立つのが、TypeScriptのPick型です。

例えば、次のようなAPIレスポンス型があるとしましょう。

type ApiResponse = {
  id: number;
  name: string;
  email: string;
  address: {
    street: string;
    city: string;
    country: string;
  };
  age: number;
  createdAt: Date;
};

このレスポンスから、特定の情報、例えばidnameaddresscityだけを抜き出すことを想定します。

その場合、Pick型を使用して次のように書くことができます。

type CustomResponse = Pick<ApiResponse, 'id' | 'name'> & {
  address: Pick<ApiResponse['address'], 'city'>;
};

このコードでは、まずApiResponseの中からidnameを抽出しています。

次に、ネストされたaddressオブジェクトからcityのみを抽出しています。

結果として、新しい型CustomResponseは以下の形になります。

type CustomResponse = {
  id: number;
  name: string;
  address: {
    city: string;
  };
};

このコードを実行すると、新しい型CustomResponseidnameaddresscityの3つのプロパティだけを持っています。

この方法を用いると、APIのレスポンス型から必要な部分だけを取得し、不要な部分を省略することが可能となります。

●注意点と対処法

TypeScriptのPickを使用する際には、多くの利点がありますが、注意点も存在します。

ここでは、その注意点や対処法について詳しく解説していきます。

○型安全性の確保

TypeScriptのPick型は、型から特定のプロパティを抽出する強力なツールですが、誤って存在しないプロパティを指定してしまうと、型エラーが発生する可能性があります。

例えば、次のようなUser型があるとします。

interface User {
  id: number;
  name: string;
  email: string;
}

ここでPick<User, 'id' | 'age'>というように、存在しないageプロパティを抽出しようとするとエラーが発生します。

このコードでは、User型からidageプロパティを抽出しようとしていますが、User型にはageプロパティが存在しないため、エラーとなります。

対処法としては、型の定義を確認し、正しいプロパティ名を指定することが必要です。

○Pickと他のユーティリティ型との組み合わせ

Pick型だけでなく、PartialOmitなど他のユーティリティ型と組み合わせて使用することもあります。

しかし、これらを組み合わせて使用する際には、順番や使用方法に注意が必要です。

例として、User型からidnameを取得し、その他のプロパティをオプショナルにしたい場合、次のように記述できます。

type OptionalUser = Partial<Pick<User, 'id' | 'name'>>;

このコードでは、PickUser型からidnameプロパティを抽出した後、Partialを使用してそれらのプロパティをオプショナルにしています。

○実行時のエラーへの対処法

Pickはコンパイル時の型情報にのみ影響を与え、実行時のオブジェクトの形状を変更しないため、実行時のエラーには注意が必要です。

例えば、次のようにUser型から特定のプロパティを取得する関数を考えます。

function getUserInfo(user: Pick<User, 'id' | 'name'>) {
  return {
    userId: user.id,
    userName: user.name
  };
}

この関数を使用してUser型のオブジェクトを引数に渡す場合、emailプロパティが存在してもエラーは発生しませんが、getUserInfo関数内でemailプロパティにアクセスしようとすると、実行時エラーとなります。

●カスタマイズ方法

TypeScriptのPick型は非常に便利ですが、場合によっては独自のカスタマイズが求められることもあります。

ここでは、そのようなカスタマイズの方法をいくつかのサンプルコードを通してご紹介します。

○独自のPick関数の作成

Pickはあるオブジェクトから特定のプロパティだけを取り出すための型です。

しかし、特定の条件下でしか取り出さないような独自のPick関数を作成することも可能です。

例えば、特定のプレフィックスがついたプロパティだけを取り出すPickByPrefixという関数を考えてみましょう。

type PickByPrefix<T, Prefix extends string> = {
    [K in keyof T as K extends `${Prefix}${infer Rest}` ? K : never]: T[K];
};

// 使用例
type Obj = {
    user_name: string;
    user_age: number;
    item_price: number;
};

type UserOnly = PickByPrefix<Obj, 'user_'>; 
// { user_name: string; user_age: number; }

このコードでは、PickByPrefix型を使って、Obj型からuser_プレフィックスのついたプロパティだけを取り出しています。

結果として、UserOnly型は{ user_name: string; user_age: number; }となります。

○Pickを拡張した型の定義

また、Pickの基本的な機能に何かを追加して新しい型を定義することもできます。

例として、特定のプロパティを必須にするRequiredPickという型を考えてみましょう。

type RequiredPick<T, K extends keyof T> = Required<Pick<T, K>>;

// 使用例
type Obj2 = {
    id?: number;
    name?: string;
};

type EssentialProps = RequiredPick<Obj2, 'id'>; 
// { id: number; name?: string; }

このコードでは、RequiredPick型を定義し、それを使ってObj2型からidプロパティを必須とした新しい型を作成しています。

そのため、EssentialProps型はidが必須の型として定義されます。

まとめ

TypeScriptは静的型チェッキングを提供する強力なスクリプト言語であり、その中でも「Pick」は特定のプロパティを持つ新しい型を作成する際に非常に便利なユーティリティ型として知られています。

本記事では、このPickの詳細な使い方や応用例、さらに注意点やカスタマイズ方法までを10のサンプルコードを通じて解説しました。

この記事を通じて、その真価と使い方のヒントを得られたことを願っています。

初心者の方から経験豊富な方まで、TypeScriptの知識を一歩進めるための参考として活用していただければ幸いです。