I am trying to create a type from a readonly object, i.e.
const Actions = {
'user.crud': ['user.create', 'user.read', 'user.update', 'user.delete'],
} as const
type ActionsType = keyof typeof Actions | typeof Actions[keyof typeof Actions][number]
The above works nicely and sets up the type ActionsType
to the string literals ('user.crud', 'user.create', etc..).
However, the Actions
object above is very simplistic, instead, I really need to generate the Actions via functions. When I port the above over to being generated by a function, i.e.
// set up a function to generate all actions for the passed role
function getActions (role: string): Record<string, string[]> {
return {
[`${role}.crud`]: [`${role}.create`, `${role}.read`, `${role}.update`, `${role}.delete`],
}
}
// generate the Actions from a function
const ActionsFromFunction = {
...getActions('user'),
} as const
// set up the Actions from a readonly object with values generated by getActions()
type ActionsFromFunctionType = keyof typeof ActionsFromFunction | typeof ActionsFromFunction[keyof typeof ActionsFromFunction][number]
the type ActionsFromFunctionType
is no longer set to the string literals. Instead it is set to: string | number
and in turn type tests fail as any string is accepted.
I've put together a demo of the above:
Is there a way of generating the Actions
object via a function, whilst still maintaining the string literals within the type?
Your goal is only achievable through typescript Template literal types. They are not supported in typescript 4.0, but will be available in 4.1 version.
This is how you could do it with typescript 4.1
type CrudOperations<ROLE extends string> = [`${ROLE}.create`, `${ROLE}.read`, `${ROLE}.update`, `${ROLE}.delete`];
type GetActionsResult<ROLE extends string> = string extends ROLE // check if we can infer type
? { [k: string]: string[] } // if type is not inferable
: { [K in `${ROLE}.crud`]: CrudOperations<ROLE> };
function getActions<ROLE extends string>(role: ROLE): GetActionsResult<ROLE> {
return {
[`${role}.crud`]: [`${role}.create`, `${role}.read`, `${role}.update`, `${role}.delete`]
} as GetActionsResult<ROLE>;
}
// falls back to { string: string[] } structure
const actions = getActions('admin' as string);
// generate the Actions from a function
const ActionsFromFunction = {
...getActions('user'),
...getActions('orders'),
}
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments