やりたいこと
このような Object から
const user = {
name: 'shiraishi',
age_o: 30
}
このような型を作りたい。
type User = {
name: string
age?: number
}
解決方法
*_o
というパターンのキー名から _o
を取り除き、オプショナルに変更する型ユーティリティ関数 TransformedType
を定義します
type TransformedType<T> = {
[K in keyof T as K extends `${infer O}_o` ? O : never]?: T[K]
} & {
[K in keyof T as K extends `${string}_o` ? never : K]: T[K]
};
この関数を適用した結果が以下です。
type User = TransformedType<typeof user>
const test1: User = {
name: 'name',
age: 12
}
const test2: User = {
name: 'name',
age: 'age' // Type 'string' is not assignable to type 'number'.(2322)
}
const test3: User = {
name: 'name',
}
const test4: User = {
name: 'name',
age: undefined
}
NG パターン
最初はより短く下記のように書いていました。
type TransformedType<T> = {
[K in keyof T as K extends `${infer O}_o` ? O : K]: K extends `${string}_o` ? T[K] | undefined : T[K]
};
この書き方だと、age
が無い場合にエラーになってしまいます。
// エラー
const test3: User = {
name: 'name',
}
// OK
const test4: User = {
name: 'name',
age: undefined
}
背景
API 側のアプリケーションから自動生成されるレスポンスのサンプルの json からフロントエンドの型を自動生成しようとしたのが始まりです。
この json では optional かどうかが分からなかったため、キー名に optional かどうかが分かる情報を含めることにし、型ユーティリティ関数で型を作る方法を考えました。
参考
関連
この問題を解決する方法として、複数のレスポンスを含めた json 配列から型を生成する方法も考えました。