🚨
TypeScriptで多層構造のエラーメッセージからエラーコードの型定義を生成する
const ERROR_MESSAGES = {
admin: {
post: {
"not-found": "投稿が見つかりません。",
"invalid-type": "投稿のタイプが無効です。",
},
},
user: {
post: {
"not-found": "投稿が見つかりません。",
"invalid-type": "投稿のタイプが無効です。",
},
search: {
"invalid-query": "検索クエリが無効です。",
"no-results": "検索結果が見つかりません。",
},
},
};
// エラーコード使用例
throw new ApplicationServiceError('admin/post/not-found'); // "admin/post/not-found" はエラーコード
上の例のように、多層構造のエラーメッセージを管理するとする。
ApplicationServiceErrorのインスタンス生成時にエラーコードを与えるとき、未定義のエラーコードを使っている場合はエラーになるようにしたいが、エラーコードを別途一つづつ定義するのは面倒。
かといって
type ErrorCode =
| "admin/post/not-found"
| "admin/post/invalid-type"
| "user/post/not-found"
| "user/post/invalid-type"
| "user/search/invalid-query"
| "user/search/no-results";
こんな型定義を別途管理する手間を省きたい。
ERROR_MESSAGESの構造はエラーコードの定義を兼ねることができそうなのでどうにかできるはず。
解決策
// ERROR_MESSAGESの構造を再帰的に表現しておく
type ErrorMessagesStruct = {
[key: string]: string | ErrorMessagesStruct;
};
// 再帰的にキーを結合してパスを作る
type JoinKeys<T, Prefix extends string = ""> = {
[K in keyof T]: T[K] extends ErrorMessagesStruct
? JoinKeys<T[K], `${Prefix}${K & string}/`>
: `${Prefix}${K & string}`;
}[keyof T];
type ErrorCodes = JoinKeys<typeof ERROR_MESSAGES>;
export const code1: ErrorCodes = "admin/post/not-found"; // OK
export const code2: ErrorCodes = "user/search/no-results"; // OK
export const code3: ErrorCodes = "admin/post/nonexistent-error-code"; // エラーになる
これでよさそう。
Discussion