I want to use vistor pattern to make sure that all cases are handled when I add a new enum value. Enum example:
export enum ActionItemTypeEnum {
AccountManager = 0,
Affiliate = 4,
}
Currently, I'm using the following approach:
interface IActionItemTypeVisitor<T> {
accountManager(value: ActionItemTypeEnum.AccountManager): T;
affiliate(value: ActionItemTypeEnum.Affiliate): T;
}
// generates compiler error when case is missing:
const visitActionItemType = <T>(value: ActionItemTypeEnum, visitor: IActionItemTypeVisitor<T>) => {
// eslint-disable-next-line
switch (value) {
case ActionItemTypeEnum.None:
return visitor.none(value);
case ActionItemTypeEnum.AccountManager:
return visitor.accountManager(value);
}
};
// ActionItemPermissionVisitor implements IActionItemTypeVisitor<Permission>
const permission = visitActionItemType(this.type, new ActionItemPermissionVisitor())
I'm looking for a more idiomatic TypeScript solution if that exists.
This is final solution:
export type IActionItemTypeVisitor<T> = {
[K in keyof typeof ActionItemTypeEnum as Uncapitalize<K>]: (value: (typeof ActionItemTypeEnum)[K]) => T;
};
export const visitActionItemType = <T>(value: ActionItemTypeEnum, visitor: IActionItemTypeVisitor<T>): T => {
const key: string = ActionItemTypeEnum[value]?.[0].toLowerCase() + ActionItemTypeEnum[value]?.substring(1);
return visitor[key]?.(value);
};
export class ActionItemVisitor implements IActionItemTypeVisitor<string> {
public accountManager(value: ActionItemTypeEnum.AccountManager): string {
return 'AccountManager';
}
public affiliate(value: ActionItemTypeEnum.Affiliate): string {
return 'Affiliate';
}
}
const result: string = visitActionItemType(ActionItemTypeEnum.AccountManager, new ActionItemVisitor());