【问题标题】:Convert enum to object with value将枚举转换为具有值的对象
【发布时间】:2022-01-09 12:53:17
【问题描述】:

我想创建一个可以返回类似对象/枚举值的助手,这样我们就不会重复模式:

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

}

如果我在这里开始使用这个枚举,我需要一个可以将其转换为以下内容的辅助方法:

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

甚至不知道从哪里开始我对打字稿很陌生

actions 的完整预期输出应该是:

{
    Find: {
        Request: 'FindRequest',
        Success: 'FindSuccess',
        Failure: 'FindFailure'
    },
    Create: {
        Request: 'CreateRequest',
        Success: 'CreateSuccess',
        Failure: 'CreateFailure'
    },
    Update: {
        Request: 'UpdateRequest',
        Success: 'UpdateSuccess',
        Failure: 'UpdateFailure'
    }
}

【问题讨论】:

  • 那么,createActions 应该结合两个枚举的键并将它们帕斯卡大小写?结果对象会包含 9 个属性吗?
  • 没错@RameshReddy!我会更新预期的输出
  • 您愿意使用string enums吗?

标签: javascript typescript


【解决方案1】:

这是一个类型安全的功能解决方案,它接受两个 string enums 作为参数(因此您可以在其他情况下重用它):

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

【讨论】:

  • 这太棒了!我自己尝试过这样做,但是我想让 nestAndCombine 函数成为一个实用程序,并且 RequestTypes 很少会改变所以我想要一个默认值,所以在使用该实用程序时它只是 const actions = createActions(ActionTypes); 如果我需要通过传递第二个参数来覆盖我可以的 RequestTypes
  • @ShannonHochkins 您是否要求我使用咖喱函数更新我的答案?如果是这样,我会这样做。 — 编辑:完成。这能回答你的问题吗?
  • 谢谢,但是这需要 createActions 重载函数在我们声明 ActionTypes 的同一个文件中,我们基本上想导入 createActions 并像你一样使用它,这有意义吗?
  • @ShannonHochkins 您对问题中的模块只字未提。我已经回答了你最初的问题,现在你问了一个关于 JavaScript 模块的完全不同的问题......你到底想知道什么 - 如何从模块中导出函数和类型并将其导入另一个模块?如果您想询问有关进出口的不同问题并在此处分享指向它的链接,我很乐意看一看,但在 SO 上,每个问题都应该是一次解决一个问题。跨度>
  • 我确实说过创建一个助手,有人会假设一个助手是导入的,但没关系,我会问另一个问题,没必要粗鲁
【解决方案2】:

您可以从Object.values 方法中获取枚举字符串,然后根据需要进行迭代和连接。

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);

【讨论】:

  • 这在使用 createActions 的返回时不提供任何智能感知
  • 没有为您提供预期的结果
【解决方案3】:

这是你可以做的:

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 游乐场:https://tsplay.dev/WG6bkm

【讨论】:

    【解决方案4】:

    为此编写了一个简单的函数。

    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);
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-12
      • 2020-09-06
      • 2021-04-11
      • 2021-12-23
      • 1970-01-01
      • 2017-03-22
      • 2015-09-01
      • 2020-10-07
      相关资源
      最近更新 更多