【问题标题】:Typescript filter object by value of argumentTypescript 按参数值过滤对象
【发布时间】:2023-02-06 06:18:47
【问题描述】:

我有一个受 Prisma 启发的函数,它从对象生成 SQL 查询字符串,然后发出 SQL 查询请求并返回检索到的对象。

这是代码的 typescript playground (original code, see new below) 最小复制。

我目前正在使用泛型来设置预期的输出,但它总是返回完整的对象,即使应用了 select 也是如此。

如果有的话,有没有办法返回根据提供的选择对象过滤的输出类型?我试过使用 keyof (typeof query)["select"] 但这会获取选择类型的键,而不是运行时提供的值。

更新:我在这方面取得了一些进展,我能够让 output2 和 output3 输出正确的类型,但仍然不是 output1。 这是一个带有更新代码的新typescript playground链接,我已经更新了帖子中的代码。

游乐场代码:

type ExpectedOutput = {
    aField: string;
    bField: number;
    cField: string;
    dField: number;
    eField: string;
    fField: number;
    gField: string;
}

type ObjectOrTrue<Type> = Type extends Record<string, any>
    ? { [Property in keyof Type]: ObjectOrTrue<Property> }
    : true;

async function myFunction<
    Type extends Record<string, any> = Record<string, unknown>
>(query: {
    select?: Partial<{ [Property in keyof Type]: ObjectOrTrue<Type[Property]> }>;
    from: string;
    where?: Partial<{ [Property in keyof Type]: Type[Property] }>;
    order?: Partial<{ [Property in keyof Type]: "ASC" | "DESC" }>;
    limit?: number;
    offset?: number;
}) {
  const {select} = query;

  // Simulated output of function
  if(select === undefined) {
    console.log('select is undefined');
    console.log({query});
    return {} as Type;
  }
  return {} as {[Property in keyof typeof select]: Type[Property]};
}
async function runAllTests() {
  const output1 = await myFunction<ExpectedOutput>({
    select: {
      aField: true,
      bField: true,
      cField: true,
    },
    from: 'SomeTable',
  });
  /*
  output1 type === ExpectedOutput, but I would like it to be
    {
      aField: string,
      bField: number,
      cField: string,
    }
  */
  const output2 = await myFunction({
    select: {
      aField: true,
      bField: true,
      cField: true,
    },
    from: 'SomeTable',
  });
  /*
  output2 type === {
      aField: unknown,
      bField: unknown,
      cField: unknown,
    } 
    which is what it should be.
  */
  const output3 = await myFunction<ExpectedOutput>({
    from: 'SomeTable',
  });
  /*
  output3 type === ExpectedOutput which is what it should be.
  */
}
runAllTests();

【问题讨论】:

    标签: typescript


    【解决方案1】:

    每当返回类型取决于参数类型时,我通常会求助于额外的泛型。但是,没有办法传递一个泛型并推断出其他泛型,所以我决定将 myFunction 设为工厂作为一种 hack。

    如果this proposal 通过,我们将能够恢复工厂。

    type ObjectOrTrue<Type> = Type extends Record<string, any>
      ? { [Property in keyof Type]: ObjectOrTrue<Property> }
      : true;
    
    function myFunction<
      Type extends Record<string, any> = Record<string, unknown>
    >() {
      return async <
        Select extends Partial<{ [Property in keyof Type]: ObjectOrTrue<Type[Property]> }> | undefined = undefined,
        MyReturnType = Select extends undefined
          ? Type
          : string extends keyof Type
          ? { [Property in keyof Select]: unknown }
          : { [Property in Extract<keyof Type, keyof Select>]: Type[Property] }
      >(query: {
        select?: Select;
        from: string;
        where?: Partial<{ [Property in keyof Type]: Type[Property] }>;
        order?: Partial<{ [Property in keyof Type]: "ASC" | "DESC" }>;
        limit?: number;
        offset?: number;
      }): Promise<MyReturnType> => {
        const { select } = query;
    
        // Simulated output of function
        if (select === undefined) {
          console.log('select is undefined');
          console.log({ query });
          return {} as never;
        }
    
        return {} as never;
      }
    }
    

    不得不修改runAllTests,我所做的就是在每个调用中添加()

    const output1 = await myFunction<ExpectedOutput>()({
      select: {
        aField: true,
        bField: true,
        cField: true,
      },
      from: 'SomeTable',
    });
    const output2 = await myFunction()({
      select: {
        aField: true,
        bField: true,
        cField: true,
      },
      from: 'SomeTable',
    });
    const output3 = await myFunction<ExpectedOutput>()({
      from: 'SomeTable',
    });
    

    【讨论】:

      猜你喜欢
      • 2017-09-06
      • 1970-01-01
      • 2018-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-22
      相关资源
      最近更新 更多