🧩

[Typescript] オブジェクトの特定プロパティの型を上書きする型関数をつくる

2022/04/09に公開

成果物

/**
 * 特定のプロパティを上書きする型関数
 * Overwrite<Type, OverwritingType>
 */
export type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
  T,
  keyof U
> & U;

何がしたいか

type Person = {
  firstName: string;
  lastName: string;
  age: string;
};

例えば上記の様なオブジェクトがあとすると、 age プロパティだけ型を string から number へサクッと安全に変更できる型関数を作りたい。

※ しかしプロパティの上書きは混乱を招くので、今回の例のような場合は設計を見直すべきです。

私たちの場合、ORMにPrismaを使っているのですが、Jsonデータを取得するときに、 any 型になってしまう。そういった時に、jsonデータのみを自分たちで作った型に上書きしてあげたかった。

型関数を用いない上書き方法

type Person = {
  firstName: string;
  lastName: string;
  age: string;
};

type NewPerson = Omit<Person, "age"> & { // ①
  age: string; // ②
};

問題点

  • 上書きしたいプロパティが増えるごとに ①、②に記述を追加する必要がある
  • ② のプロパティは実質なんでも追加できてしまうため、安全性に欠ける & タイポも起こり得る
  • 普通に冗長

型関数を用いた場合

/**
 * 特定のプロパティを上書きする型関数
 */
export type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
  T,
  keyof U
> & U;
type Person = {
  firstName: string;
  lastName: string;
  age: string;
};

type NewPerson = Overwrite<Person, {age: number;}>

どうでしょう、Overwriteを使うことで、安全に特定プロパティのみ上書きできたかと思います。
例えば、 age をタイポしてもちゃんとエラーがでてくれます。

Overwriteの実装はシンプルで、

  1. 受け取る関数をジェネリクスで定義
    • 第一引数は対象のオブジェクト
    • 第二引数は対象のオブジェクトの特定のプロパティだけを指定できるよう extends で制約をつくる
  2. Omitで対象プロパティを削除
  3. 第一引数と第二引数をマージして返却

終わりに

少ししらべてみましたが、このシンプルな型関数がどこにも載っていなかったので、作って公開してみました。

活用シーンは限られますが、お役に立てれば👋

✍️ iba_ragi

Discussion