【问题标题】:Is it possible for a function to have two Records with function values with the same parameters as arguments?一个函数是否有可能拥有两个具有与参数相同的参数的函数值的记录?
【发布时间】:2022-01-13 08:26:14
【问题描述】:

我正在尝试创建一个可以接收两条记录的函数,其中:

  • 第二个依赖于第一个并且两个记录具有相同的键
  • 它们都将函数作为具有相同参数但返回类型不同的值。

我尝试了以下方法(及其许多变体):

export const withGenericLogging = (
  logger: Logger,
  component: string,
  subcomponent: string,
) => <
  Keys extends string,
  ObjFn extends (...args: any[]) => unknown,
  Obj extends Record<Keys, ObjFn>,
  Mappers extends Record<Keys, (...args: Parameters<ObjFn>) => unknown>,
>(
  obj: Obj,
  mappers: Mappers,
) =>
  Object.keys(obj).reduce<Obj>(
    (acc, key) => {
      if (typeof obj[key] !== 'function') {
        return acc;
      }

      const mapper = mappers?.[key]

      acc[key] = withLogging(
        logger,
        component,
        subcomponent ? `${subcomponent}.${key}` : key,
      )(obj[key], mapper);

      return acc;
    },
    { ...obj },
  );

注意:withLoggingLoggerlogger 的实现与此问题无关。

这很好用,因为打字稿知道如果我执行以下操作:

const create = (id: string) => 'value'

const repository = withGenericLogging(
  log,
  'Module',
  'Submodule'
)(
  { create },
  { create: id => 'mappedId' },
);

会发生以下情况:

  • repository 将完全继承 obj 的类型(如预期的那样,它将是 { create: (id: string) =&gt; string } 类型)
  • IDE 将为mappers 参数提供某种类型的支持(IDE 建议{ create: id =&gt; 'mappedId' } 中的create 键)
  • id =&gt; 'mappedId' 中的idany 类型,它应该是string 类型

withGenericLogging能否提供:

  • 支持对其返回值的类型推断(在上面的示例中:repository
  • 支持mappers 的密钥类型(应与obj 的密钥相同)
  • 支持mappers记录右侧函数的参数,该参数应与obj记录右侧函数的参数相同(如@987654342 @是{ create: (id: string) =&gt; Promise&lt;void&gt; }mappers应该是{ create: (id: string) =&gt; unknown })
  • 只是一种处理类型的通用方式,而不是固定的(obj 应该支持任何类型的Record&lt;string, (...args: unknown[]) =&gt; unknown&gt;,而不是固定的type Something = { create: (id: string) =&gt; Promise&lt;void&gt; }

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    解决方案

    我找到了我的问题的解决方案(经过多次迭代):

    第 1 部分:定义通用 MapperFunction 类型

    我需要推断函数的值或参数 mappers 对象的每个键并使用它们来创建映射函数。所以我做了这个:

    type MapperFunction<T> = T extends (...args: infer Params) => unknown
      ? (...args: Params) => unknown
      : (arg: T) => unknown;
    

    当给定MapperFunction 的参数时,这允许我创建一个具有参数Params 或给定T 的函数。

    这允许我创建映射函数,例如:

    const create = (id: string) => 'value'
    const value = 'value'
    
    const mapperFunctionForCreate: MapperFunction<typeof create> = id => ({ mappedId: id })
    const mapperFunctionForValue: MapperFunction<typeof value> = arg => ({ mappedId: arg })
    

    地点:

    • mapperFunctionForCreate 的类型为 (id: string) =&gt; unknown(如预期)
    • mapperFunctionForValue 的类型为 (arg: string) =&gt; unknown(如预期)

    这解决了我的第一个问题。

    第 2 部分:定义通用 MapperFunctionsObject 类型

    通过执行以下操作解决了我的问题的第二部分(使用键 Keys 和映射器函数定义通用对象作为值):

    type MapperFunctionsObject<T> = T extends Record<string | number | symbol, unknown>
      ? { [P in keyof T]: MapperFunction<T[P]> }
      : never;
    

    这允许我执行以下操作:

    const obj = {
      create: (id: string) => 'value'
    };
    
    const mapperFunctionsObject: MapperFunctionsObject<typeof obj> = {
      create: id => ({ mappedId: id })
    };
    

    其中mapperFunctionsObject.create 的类型为(id: string) =&gt; unknown(如预期的那样)。

    这让我可以把以下函数放在一起:

    type MapperFunction<T> = T extends (...args: infer Params) => unknown
      ? (...args: Params) => unknown
      : (arg: T) => unknown;
    
    type MapperFunctionsObject<T> = T extends Record<string | number | symbol, unknown>
      ? { [P in keyof T]: MapperFunction<T[P]> }
      : never;
    
    export const withGenericLogging = (
      logger: Logger,
      component: string,
      subcomponent?: string,
    ) => <
      Key extends string,
      Value,
      Obj extends Record<Key, Value>,
      MapperObj extends MapperFunctionsObject<Obj>
    >(
      obj: Obj,
      mappers?: Partial<MapperObj>,
    ) =>
      Object.keys(obj).reduce<Obj>(
        (acc, key) => {
          if (typeof obj[key] !== 'function') {
            return acc;
          }
    
          const func = obj[key];
          const mapper = mappers?.[key];
    
          acc[key] = withLogging(
            logger,
            component,
            subcomponent ? `${subcomponent}.${key}` : key,
          )(func, mapper);
    
          return acc;
        },
        { ...obj },
      );
    

    这正是我需要做的,例如当我执行以下操作时:

    const create = (id: string) => 'value'
    
    const repository = withGenericLogging(
      log,
      'Module',
      'Submodule'
    )(
      { create },
      { create: id => 'mappedId' },
    );
    

    { create: id =&gt; 'mappedId' } 的类型将被正确推断,并且将是 { create: (id: string) =&gt; unknown }(如预期)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-28
      • 1970-01-01
      • 2017-07-19
      • 2016-04-19
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多