需要帮助来转换打字稿助手

Need help to convert typescript helper

我有一个帮手,我正在导出和导入多个文件,它工作正常,但我想稍微改变一下,我正在努力解决这个问题:

// utils.ts
export enum RequestTypes {
  Request,
  Success,
  Failure,
}

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

export function createActions <P, C>(actionTypes: P, requestTypes: C): CombineEnums<P, C> {
  const result: any = {};
  for (const keyP of Object.keys(actionTypes)) {
    result[keyP] = {};
    for (const keyC of Object.keys(requestTypes)) {
      result[keyP][keyC] = `${keyP}${keyC}`;
    }
  }
  return result;
}

现在在另一个文件中,我正在导入 createActions 方法和 RequestTypes 并像这样使用它:

// module.ts
import { createActions, RequestTypes } from './utils.ts';
enum Actions {
   Find,
   Create
}
const actions = createActions(Actions, RequestTypes);
// actions.Find.Request -> 'FindRequest'
// actions.Find.Success -> 'FindSuccess'
// actions.Create.Failure -> 'CreateFailure'

这一切都很好,但是 RequestTypes 很少会改变,我想让它成为一个可选参数,但如果需要仍然能够通过其他 RequestType。

所以理想情况下,我喜欢上面的解决方案,但用法是:

const actions = createActions(Actions) 具有相同的输出。

如果需要,通过不同的请求类型:

enum TabRequests {
   Open,
   Close
}
enum TabActions {
   Tab
}
const actions = createActions(TabActions, TabRequests);
// actions.Tab.Open -> 'TabOpen'
// actions.Tab.Close -> 'TabClose'

我已经尝试了所有我能想到的方法,但似乎无法弄清楚它的打字稿方面!

你可以使用打字稿 overload signatures, also see the playground:

// signature for only one argument
export function createActions <P>(actionTypes: P): CombineEnums<P, typeof RequestTypes>;

// signature for two arguments
export function createActions <P, C>(actionTypes: P, requestTypes: C): CombineEnums<P, C>;

// actual implementation with default value for second argument
export function createActions <P>(actionTypes: P, requestTypes: any = RequestTypes): CombineEnums<P, any> {
  const result: any = {};
  for (const keyP of Object.keys(actionTypes)) {
    result[keyP] = {};
    for (const keyC of Object.keys(requestTypes)) {
      result[keyP][keyC] = `${keyP}${keyC}`;
    }
  }
  return result;
}

// If you want you could also use the following line as implementation signature
// export function createActions <P, C>(actionTypes: P, requestTypes: C | typeof RequestTypes = RequestTypes): CombineEnums<P, C | typeof RequestTypes> {


// Now you can use both signatures, typesafe.
const actionsWithRequestTypeArgument = createActions(Actions, RequestTypes);
const actionsWithoutRequestTypeArgument = createActions(Actions);

以下是如何在单独的模块中使用 中的重载函数:

TS Playground link

// utils.ts

type StringEnum = Record<string, string>;

export 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;
  };
};

export 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;
}

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

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

// module.ts

import {createActions, RequestType} from './utils.ts';

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