🐱
【小ネタ】定数オブジェクトのプロパティを再起探索してユニオン型を作る・応用例紹介
結論
原案
type ObjectKeyDeep<T> = T extends object
? { [K in keyof T]: ObjectKeyDeep<T[K]> }[keyof T]
: T;
改良版
type ObjectKeyDeep<T> = T extends Record<string, infer U> ? ObjectKeyDeep<U> : T;
応用例
RouterやStoreなどの文字列定数をキーとして使う関数を型安全にする
(Vue の例です)
- push のラップ関数を作る
router.ts
const ROUTE_NAMES = {
HOME: "home",
ABOUT: "about",
BLOG: {
LIST: "blog.list",
DETAIL: "blog.detail",
}
} as const;
new VueRouter({
routes: [
{
path: "/",
name: ROUTE_NAMES.HOME,
component: Home,
},
{
path: "/about",
name: ROUTE_NAMES.ABOUT,
component: About,
},
{
path: "/blog",
name: ROUTE_NAMES.BLOG.LIST,
component: BlogList,
},
{
path: "/blog/:id",
name: ROUTE_NAMES.BLOG.DETAIL,
component: BlogDetail,
},
],
});
type RouteName = ObjectKeyDeep<typeof ROUTE_NAMES>;
const typedPush = (name: RouteName, params?: Record<string, string>) =>
router.push({ name, params });
typedPush(ROUTE_NAMES.BLOG.DETAIL); // OK
typedPush("blog.list"); // OK
typedPush("hogehoge"); // NG (Type '"hogehoge"' is not assignable to type 'RouteName'.)
Router の第一引数の Location
型は name
の型が string
なので、RouteName
で declare したほうが賢明...?
以上しょぼすぎるけど割と実務で使えそうなやつでした。
Discussion