Omit,Pickで適切に型を定義する

TypeScriptにはユーティリティ型(Utility Types)が提供されています。

このユーティリティ型の中には様々なものが存在しますが、今回はすでに存在する型から新たな型を定義することができる"Omit"と"Pick"についてご紹介します。

Omit

Omitはすでに存在する型から除外するものを選択し、それ以外のプロパティから新たに型を作成するユーティリティ型となります。

Omit<Type, Keys>

Typeにはオブジェクト型を渡します。以下の例ではinterfaceですがTypeでも良いです。

Keysには除外するプロパティーキーをstringで指定します。

interface Todo {
  id: string,
  title: string;
  description: string;
}
 
type TodoPreview = Omit<Todo, "id">;
 
const todo: TodoPreview = {
  title: "Workouts",
  description: "Benchpress"
};

console.log(todo)
// [LOG]: {
//   "title": "Workouts",
//   "description": "Benchpress"
// } 

Pick

Pickはすでに存在する型から指定したプロパティを取り出して新たに型を作成するユーティリティ型となります。

Pick<Type, Keys>

Typeにはオブジェクト型を渡します。以下の例ではinterfaceですがTypeでも良いです。

Keysには取り出すプロパティーキーをstringで指定します。

interface Todo {
  id: string,
  title: string;
  description: string;
}
 
type TodoPreview = Pick<Todo, "title" | "description">;
 
const todo: TodoPreview = {
  title: "Workouts",
  description: "Benchpress"
};

console.log(todo)
// [LOG]: {
//   "title": "Workouts",
//   "description": "Benchpress"
// } 

活用例

以下のように扱うと良いのではないかと私は思いました。

データモデルとしてのインターフェースTodoに対して、idの情報は画面表示等では必要のない情報となるためidを除外したタイプTodoPreviewを作成しています。

次に関数getTodoは指定したTODOリストのIDの情報を取得します。この関数の戻り値の型指定をTodoPreviewとすることで関数の型の指定を見るだけで何がしたいのかが分かり、ある種ドキュメントのような役割を果たしてくれます。(TypeScriptを使うメリットを享受できますね。)

また、関数getTodoのパラメータのtodoId: Todo['id']についてですがこれはLookup型という記法です。これはオブジェクト型に対してパラメータを指定してアクセスできます。今回の場合は引数をただstringと指定してしまうのではなく、TODOリストのIDの型としてTodo['id']としてあげることでより分かりやすく表現できるかと思います。

Lookup型を使うメリットとして元のオブジェクト型のプロパティの型が変更されても直接関数等に対して指定する必要がないというメリットがあります。例えば今回の場合だとidの型がstringからnumberに変更されるなどです。(滅多にないと思いますが)

interface Todo {
  id: string,
  title: string;
  description: string;
}
 
type TodoPreview = Omit<Todo, "id">;

// TODOリスト
const todoList: Array<Todo> = [
  {
    id: "1",
    title: "Hang out the laundry",
    description: "Take in the laundry and hang it out to dry"
  },
  {
    id: "2",
    title: "Go grocery shopping",
    description: "Stock up on necessary groceries at the supermarket"
  },
  {
    id: "3",
    title: "Read an English book",
    description: "Read the English novel I recently bought"
  },
]

// 指定したIDのTODOリストの項目を画面表示のために取得する
const getTodo = (todoId: Todo['id']): TodoPreview | null => {

    const todo: Todo | undefined = todoList.find(v => v.id === todoId)
    const todoPreview: TodoPreview | null = todo ? {title: todo.title, description: todo.description} : null
    
    return todoPreview
}

// 画面イベント等でユーザー入力があったものとする
const inputUserId: Todo['id'] = "2"
// リスト項目を取得
const todoPreview: TodoPreview | null = getTodo(inputUserId)

console.log(todoPreview)
// [LOG]: {
//   "title": "Go grocery shopping",
//   "description": "Stock up on necessary groceries at the supermarket"
// }