1

I would like to create a helper that can return an object/enum like value so we're not repeating patterns:

enum ActionTypes {
    Find,
    Update,
    Create
}
enum RequestTypes {
    Request,
    Success,
    Failure

}

If i was starting with this enum here, I need a helper method that can convert this to the following:

const actions = createActions(ActionTypes);
// actions.Find.Request -> 'FindRequest'
// actions.Find.Success -> 'FindSuccess'

Not even sure where to begin i'm pretty novice at typescript

The full expected output of actions should be:

{
    Find: {
        Request: 'FindRequest',
        Success: 'FindSuccess',
        Failure: 'FindFailure'
    },
    Create: {
        Request: 'CreateRequest',
        Success: 'CreateSuccess',
        Failure: 'CreateFailure'
    },
    Update: {
        Request: 'UpdateRequest',
        Success: 'UpdateSuccess',
        Failure: 'UpdateFailure'
    }
}
3
  • so, createActions should combine the keys of two enums and pascal case them? Will the resultant object contain 9 properties? Commented Dec 3, 2021 at 4:23
  • That's correct @RameshReddy! I'll udpate the expected output Commented Dec 3, 2021 at 4:26
  • Are you open to using string enums? Commented Dec 3, 2021 at 4:46

4 Answers 4

1

Here's a type-safe functional solution that accepts two string enums as parameters (so you can reuse it for other cases):

TS Playground link

type StringEnum = Record<string, string>;

type NestedCombinedStringEnum<
  P extends StringEnum,
  C extends StringEnum,
> = {
  [KP in keyof P]: {
    [KC in keyof C]: KP extends string ? KC extends string ? `${KP}${KC}` : never : never;
  };
};

function nestAndCombineStringEnums <
  P extends StringEnum,
  C extends StringEnum,
>(enumParent: P, enumChild: C): NestedCombinedStringEnum<P, C> {
  const result: any = {};

  for (const keyP of Object.keys(enumParent)) {
    result[keyP] = {};
    for (const keyC of Object.keys(enumChild)) {
      result[keyP][keyC] = `${keyP}${keyC}`;
    }
  }

  return result;
}

// Use:

enum RequestType {
  Request = 'Request',
  Success = 'Success',
  Failure = 'Failure',
}

// Overloaded function declaration (classic)

// function createActions <
//   P extends StringEnum,
//   C extends StringEnum,
// >(enumParent: P, enumChild: C): NestedCombinedStringEnum<P, C>;
// function createActions <P extends StringEnum>(enumParent: P): NestedCombinedStringEnum<P, typeof RequestType>;
// function createActions <
//   P extends StringEnum,
//   C extends StringEnum,
// >(enumParent: P, enumChild?: C) {
//   return nestAndCombineStringEnums(enumParent, enumChild ?? RequestType);
// }

// Overloaded function expression (you'll see this style less often, but the two are equivalent)

const createActions: {
  <P extends StringEnum, C extends StringEnum>(enumParent: P, enumChild: C): NestedCombinedStringEnum<P, C>;
  <P extends StringEnum>(enumParent: P): NestedCombinedStringEnum<P, typeof RequestType>;
} = <C extends StringEnum, P extends StringEnum>(
  enumParent: P,
  enumChild?: C,
) => nestAndCombineStringEnums(enumParent, enumChild ?? RequestType);

enum ActionType {
  Find = 'Find',
  Update = 'Update',
  Create = 'Create',
}

const actions = createActions(ActionType); // same as createActions(ActionType, RequestType);
console.log(actions.Create.Success); // "CreateSuccess"
console.log(actions); // the expected result object in your question
Sign up to request clarification or add additional context in comments.

6 Comments

This is great! I've tried to do this myself, however I wanted to have the nestAndCombine function a utility, and the RequestTypes will rarely change so I wanted to have a default value so when using the utility it's just const actions = createActions(ActionTypes); and if i need to overwrite the RequestTypes i can by passing the second argument
@ShannonHochkins Are you asking for me to update my answer with a curried function? If so, I'll do that. — Edit: Done. Does this answer your question?
Thankyou, however this requires the createActions overloaded function to be in the same file we're declaring the ActionTypes, we basically wanted to import createActions and use it like you have, does this make sense?
@ShannonHochkins You said nothing about about modules in the question. I've answered your original question, and now you're asking a completely different question about JavaScript modules... What exactly do you want to know -- how to export a function and a type from a module and import it into another module? If you'd like to ask a different question about imports and exports and share a link to it here, I'd be happy to take a look, but on SO, each question should be about solving one problem at a time.
I did say create a helper, one would assume a helper is imported but that's okay, i'll ask another question, no need to be rude about it
|
1

you can get the enum strings from Object.values method then iterate and concatenate as you want.

enum ActionTypes {
  Find,
  Update,
  Create
}
enum RequestTypes {
  Request,
  Success,
  Failure

}


function createActions(actionTypes) {


  let actionTypeStrings = Object.values(actionTypes).filter(e => typeof e == 'string');
  let requestTypeStrings = Object.values(RequestTypes).filter(e => typeof e == 'string');

  let actions = actionTypeStrings.reduce((a, c) => {

    let anAction: any = {};
    requestTypeStrings.forEach((e: any) => anAction[e] = c + e)

    a[c] = anAction;

    return a;
  }, < any > {})

  return actions;
}


const actions = createActions(ActionTypes);
console.log(actions);

2 Comments

This doesn't provide any intellisense when using the return of createActions
isn't provide you the expected results
0

This is what you can do:

enum ActionTypes {
    Find = 'Find',
    Update = 'Update',
    Create = 'Create'
}

enum RequestTypes {
    Request = 'Request',
    Success = 'Success',
    Failure  = 'Failure'

}

type combination = {
  [k in  ActionTypes]: {
    [k1 in  RequestTypes]: `${k}${k1}`
  }
}


function createActions(): combination {
  return Object.keys(ActionTypes).reduce((cum, k) => {
     cum[k] = Object.keys(RequestTypes).reduce((cum1, k1) => {
       cum1[k1] = `${k}${k1}`
       return cum1;
     }, {} as any)
     return cum;
  } , {} as any)
}


console.log(createActions().Create.Failure)
// CreateFailure

TS Playground: https://tsplay.dev/WG6bkm

Comments

0

Wrote a simple function to do this.

function mixEnumsToObject(e1: any, e2: any) {
  const keys1: string[] = Object.keys(e1).filter((key) => isNaN(Number(key)));
  const keys2: string[] = Object.keys(e2).filter((key) => isNaN(Number(key)));
  const mixEnumMap = new Map();

  keys1.forEach((key1) => {
    const valueObject = new Map();
    keys2.forEach((key2) => {
      valueObject.set(key2, key1 + key2);
    });
    mixEnumMap.set(key1, valueObject);
  });
  return mixEnumMap;
}

const mixedEnumObject = mixEnumsToObject(ActionTypes, RequestTypes);
console.log(mixedEnumObject);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.