Is there a way to dynamically generate enums on TypeScript based on Object Keys?

I'm defining an object and I want to dynamically generate enum based on its keys, so I get IDE suggestions and do not call wrong keys.

const appRoutes = {
   Login,
   Auth,
   NotFound
} 

enum AppRoutes = {[Key in keyof appRoutes]: [keyof appRoutes]}

Solution 1:

You can't build an actual enum from the object keys.

You can get a union of all keys with just keyof typeof appRoutes and that will have the type safe effect you want:

type AppRoutes = keyof typeof appRoutes

let ok: AppRoutes = "Auth";
let err: AppRoutes = "Authh";

An enum is not just a type though, it's also a runtime object that contains the keys and values of the enum. Typescript does not offer a way to automatically create such an object from a string union. We can however create a type that will ensure that the keys of an object and the members of the union stay in sync and we get a compiler error if t hey are not in sync:

type AppRoutes = keyof typeof appRoutes
const AppRoutes: { [P in AppRoutes]: P } = {
    Auth : "Auth",
    Login: "Login",
    NotFound: "NotFound" // error if we forgot one 
    // NotFound2: "NotFound2" // err
}
let ok: AppRoutes = AppRoutes.Auth;

Solution 2:

I use this workaround to convert the keys to a const key -> key object:

const { Login, Auth, NotFound } = {} as any
const appRoutes = {
  Login,
  Auth,
  NotFound
}

const AppRoutes = (() => ({
 ...Object.keys(appRoutes)
   .filter(k => isNaN(Number(k)))
   .reduce((acc, cur) => ({
     ...acc,
     [cur]: cur,
   }), {}),
} as  {
 [k in keyof typeof appRoutes]: k
}))()

console.info(AppRoutes)
// AppRoutes: {
//   Login: "Login";
//   Auth: "Auth";
//   NotFound: "NotFound";
// }