はじめに
ある日、こんなことが起こった。
サーバーサイドから送られてくるオブジェクトの一部のプロパティがstringの時とnumberの時があるやん。。
例
const user = {
id: 1,
name: 'james',
age: 20,
}
const user = {
id: 1,
name: 'mike',
age: '20'
}
例ではage
プロパティがnumber
の時とstring
の時があり、「これどちらかに揃えてくれよ〜〜」と思ったがサーバーサイドの方曰く、何かしらの理由でしょうがないらしい。。
普段typescriptで開発しているし、ageがstringの時とnumberの時でそれぞれ型を定義するのは良くないから、どうしようと思った時の対処法を考えてみた。
案① それぞれ型を作成する
これは普通に良くない。
type User = {
id: number;
name: string;
age: number;
}
type User2 = {
id: number;
name: string;
age: string;
}
案② Omitを使う
- 冗長
-
age: string
の部分は実質なんでも追加できるため、ageをタイポしてもわからない - 他にも上書きしたい時が出てきたらまた作成しないといけない。
type User = {
id: number;
name: string;
age: number;
}
type OmitUser = Omit<User, 'age'> & {
ag: string // タイポしてもわからない
}
const user: OmitUser = {
id: 1,
name: 'mike',
age: '20' // ここで初めてタイポしてることに気づく
}
結論
ジェネリックス + Omitで型関数を作成してみた。
ぱっと見てわかりやすい!!
type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
T,
keyof U
> & U;
const user2:Overwrite<User, {age: string}> = {
id: 1,
name: 'mike',
age: '20'
}
const user3:Overwrite<User, {age: string[]}> = {
id: 1,
name: 'mike',
age: ['20', '21'] // ageが配列になることはありえないけど、例として作成してます。
}
解説
上書きする関数をジェネリックスとOmitで定義しています。
ここではT
にUser型
が入り、U
には変換したいプロパティと変換したい型を定義しています。
extends
を使用し、keyof T
つまりUser型(T)のキー
のみを受け入れるように制限しています。
そして、Omitで指定したT(User型のキー)
からkeyof U(ageプロパティ)
を削除し、新たに
U(age: string)
を追加しているといった感じですね。
これで一部のプロパティの型を上書きする関数を作成できました。
type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
T,
keyof U
> & U;
Overwrite<User, {age: string}> // タイポするとエラー出ます。
おわりに
共通の処理をジェネリックス等を使用してまとめた関数を作成するとtypescript見習いから
初心者になれた気がします。