👀

ユーティリティ型【個人学習まとめ】

に公開

ユーティリティ型

型を変換したり、新しい型を生成するために使用する組み込み式のジェネリック型が TypeScript には存在し、ユーティリティ型と呼びます。
いくつもの種類がありますが、今回は私が気になった 3 つの学習をしてみましょう!

Partial<T>型

Partial<T>型は、指定された型のプロパティをオプショナルプロパティ(任意のプロパティ)に変換します。

例として、Userインターフェイスを用意します。
ageaddressはオプショナルプロパティで、省略しても OK なプロパティです。

interface User {
  name: string;
  email: string;
  age?: number;
  address?: string;
}

Userインターフェイスを使用して、変数user_1を宣言し初期化します。

let user_1: User = {
  name: "○○",
  email: "abc_123@email.com",
  age: 20,
  address: "",
};

さらに、関数updateUserInfoを定義します。この関数の第二引数にPartialを適用します。

function updateUserInfo(user: User, updateField: Partial<User>) {
  return { ...user, ...updateField };
}

Partialを適用したことによって、関数updateUserInfo内ではUserインターフェイスすべてのプロパティがオプショナルプロパティになります。

これにより、Userインターフェイスで必須のプロパティであるnameemailを指定せずとも、更新したいプロパティだけを更新できるようになりました。
実際に更新してみましょう!

//更新前
→ { name: '○○', email: 'abc_123@email.com', age: 20, address: '' }

//emailだけ更新
user_1 = updateUserInfo(user_1, {
  email: "ABC_DEF@email.com",
});

console.log(user_1);
→ { name: '○○', email: 'ABC_DEF@email.com', age: 20, address: '' }

//addressだけ更新
user_1 = updateUserInfo(user_1, {
  address: "xx県",
});

console.log(user_1);
→ { name: '○○', email: 'ABC_DEF@email.com', age: 20, address: 'xx県' }

上記の例では、必須プロパティであるnameを指定しなくてもemailaddressが更新できていることが確認出来ました。
Partialを利用せずにuser_1の一部だけを更新使用とするとエラーになります。

function updateUserInfo2(user: User, updateField: User) {
  return { ...user, ...updateField };
}

//emailだけ更新
user_1 = updateUserInfo2(user_1, {
  email: "ABC_DEF@email.com",
  → 型 '{ email: string; }' の引数を型 'User' のパラメーターに割り当てることはできません。
  プロパティ 'name' は型 '{ email: string; }' にありませんが、型 'User' では必須です。
});

Partialを利用しない場合は、たとえ 1 部のプロパティだけを更新したい時でも、すべてのプロパティを記述する必要があります。


//emailだけ更新
user_1 = updateUserInfo2(user_1, {
  name: "○○",
  email: "abc_123@email.com",
  age: 20,
  address: "ABC_DEF@email.com",
});
→ エラーなし

Record<Key,Type>型

キーの型がKey、値の型がTypeであるオブジェクトを構成するためのユーティリティ型です。

type primaryColors = "red" | "green" | "blue";

let color: Record<primaryColors, string>;

color = {
  red: "FF0000",
  green: "00FF00",
  blue: "0000FF",
};

上記の例だと型が

  • red
  • green
  • blue
    の 3 つになります。値はstring型のcolorオブジェクトを定義しています。
    ちなみに、型primaryColorsに存在しないプロパティを定義しようとしてもエラーになります。
color = {
  red: "FF0000",
  green: "00FF00",
  blue: "0000FF",
  purple: "a260bf",
  → オブジェクト リテラルは既知のプロパティのみ指定できます。'purple' は型 'Record<primaryColors, string>' に存在しません
};

Pick<T,Keys>型

既存の型Tからいくつかのプロパティkeysを選択して、新しい型を構成するために使用されます。

interface User {
  name: string;
  email: string;
  age: number;
  address: string;
  birthdate: Date;
}

type Person = Pick<User, "name" | "age" | "birthdate">;

const teacher: Person = {
  name: "A先生",
  age: 30,
  birthdate: new Date("1995/10/10"),
};

上記の例では、Userインターフェイスからnameagebirthdateプロパティのみを選択して、新しいPersonという型を作成しています。
変数teacherは新しく作成したPerson型なので、nameagebirthdateのみを代入することができます。
もちろん他のプロパティを代入することはできません。

const teacher: Person = {
  name: "A先生",
  age: 30,
  birthdate: new Date("1995/10/10"),
  email:"xxxxxx"
  → オブジェクト リテラルは既知のプロパティのみ指定できます。'email' は型 'Person' に存在しません。
};

練習問題

今回も練習問題をchatGPTに作ってもらいました。気になる方はどうぞ~

問題 1:Partialを使って関数を完成させよう

以下のインターフェースと関数の型をもとに、updateBook関数がエラーなく一部プロパティのみを更新できるようにしましょう。

interface Book {
  title: string;
  author: string;
  year: number;
}

function updateBook(book: Book, update: ???): Book {
  return { ...book, ...update };
}

???には何を入れたら良いでしょうか?

回答
console.log("-- 問題1 --");
interface Book {
  title: string;
  author: string;
  year: number;
}

function updateBook(book: Book, update: Partial<Book>): Book {
  return { ...book, ...update };
}

let testData1: Book = {
  title: "○○",
  author: "xx先生",
  year: 2025,
};

console.log(
  updateBook(testData1, {
    author: "A先生",
  })
);

問題 2:Recordを使ってオブジェクトを型定義しよう

以下のようなstatusMessagesオブジェクトを作成したいです。"success" | "error" | "loading"という 3 つのステータスをキーに持ち、それぞれに文字列のメッセージが設定されているようにしてください。

// この変数の型を Record を使って定義してください
let statusMessages: ???;

statusMessages = {
  success: "操作が完了しました",
  error: "エラーが発生しました",
  loading: "読み込み中です",
};

???の部分を考えてみましょう。

回答
console.log("-- 問題2 --");
type statusType = "success" | "error" | "loading";

let statusMessages: Record<statusType, string>;

statusMessages = {
  success: "操作が完了しました",
  error: "エラーが発生しました",
  loading: "読み込み中です",
};

console.log(statusMessages);

問題 3:Pick を使って一部のプロパティだけを取り出そう

以下のような Product インターフェースがあります。

interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
}

この Product 型から、idname のみを持つ SimpleProduct 型を Pick を使って定義してください。

その型を使って、以下のような productInfo 変数を定義してみましょう。

// ↓ここに Pick を使って SimpleProduct 型を定義してください

const productInfo: SimpleProduct = {
  id: 1,
  name: "ノートパソコン",
};

descriptionprice を含めないように注意してくださいね!

回答
console.log("-- 問題3 --");
interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
}

type SimpleProduct = Pick<Product, "id" | "name">;

const productInfo: SimpleProduct = {
  id: 1,
  name: "ノートパソコン",
};

console.log(productInfo);

Discussion