TypeScriptでオブエジェクトのプロパティの型を変換する

課題

オブジェクトの型で、プロパティの追加や削除ではなく、存在する型を上書きしたい場合はどうすればよいでしょうか?

Personという型を用意して、birthdayというプロパティの型を、 Dateからstringに変更する場合を考えてみます。

interface Person {
  id: number
  name: string
  birthday: Date
}

継承してみる

まず、extendsを使って、継承してみましょう

interface Person2 extends Person {
  birthday: string
}

すると、Datestringは適用できないと、エラーが表示されます。

Interface 'Person2' incorrectly extends interface 'Person'.
  Types of property 'birthday' are incompatible.
    Type 'string' is not assignable to type 'Date'.ts(2430)

解決方法

プロパティの型を変換する汎用的な型を作成する

// プロパティの型変換
type ConvertPropetyType<T, E> =
  Pick<T, Exclude<keyof T, keyof E>> &
    Pick<E, Extract<keyof E, keyof T>>

ジェネリクスの第1引数に変換元の型を指定、第2引数に、変換したいプロパティと型を指定します。

変換した型を作成してみます。

type Person2 = ConvertPropetyType<Person, { birthday: string }>

今度はエラーは発生しません。

実際に、オブジェクトに適用してみます。

const person1: Person2 = {
  id: 1,
  name: 'name',
  birthday: new Date(),
}

変換前のDate型を実装すると、Type 'Date' is not assignable to type 'string'.ts(2322)というエラーが発生します。

const person1: Person2 = {
  id: 1,
  name: 'name',
  birthday: '2000-01-01',
}

変換後のstringで実装すると、エラーが発生せず、期待通りに型が適用できました。