首页 > 解决方案 > 从对象属性推断类型

问题描述

是否可以创建一个从“外部”对象属性推断类型的通用函数?

想象一下这样的事情:

const resolversMap: {
  overallRankingPlacement: (issuer: number, args: Record<string, any>, context: Record<string, any>) => Promise<number>;
} = {
  overallRankingPlacement: createResolver((issuer, args, context) => {
    return 0;
  }),
};

function createResolver<T extends () => void>(resolverFn: T): ReturnType<T> {
  return (...args) => {
    // Some additional logic...
    resolverFn(...args);
  };
}

目标是使返回的函数createResolver与“overallRankingPlacement”的确切返回相匹配,并且传递给它的参数将完全匹配——一种镜像/代理与类型有关的所有内容。

标签: typescripttyping

解决方案


您可以通过对参数数组和结果类型使用泛型参数来做到这一点。像这样的东西会起作用:

function createResolver<Args extends unknown[], Res>(
  resolverFn: (...args: Args) => Res
): (...args: Args) => Res {
  return (...args: Args) => {
    // Some additional logic...
    return resolverFn(...args);
  };
}

但是,上面的resolversMap示例仍然会失败,因为overallRankingPlacement函数返回了一个 Promise,而 to 的参数createResolver却没有。Promise您可以通过从返回类型中删除或传递异步函数来修复它createResolver以获取符合要求的类型。

也许您正在寻找的是一个createResolver返回异步函数的函数?如果是这样,您可以在上面的函数中添加 aPromise和 anasync并获得:

function createAsyncResolver<Args extends unknown[], Res>(
  resolverFn: (...args: Args) => Res
): (...args: Args) => Promise<Res> {
  return async (...args: Args) => {
    // Some additional async logic...
    return resolverFn(...args);
  };
}

此版本将与您的示例一起正常工作resolversMap

值得注意的是,像这样包装函数并不能很好地处理重载函数。只有最后一个重载被保留,所以对于

function overloaded(x: number): number
function overloaded(x: string): string
function overloaded(x: number | string) {
    return x
}

包装的函数只会从stringto string

const unfortunatelyNotOverloaded = createResolver(overloaded)
const ok = unfortunatelyNotOverloaded('ok') // inferred type: string
const fail = unfortunatelyNotOverloaded(1) 
// ERROR: Argument of type 'number' is not assignable to parameter of type 'string'.

我认为目前还没有任何解决办法,甚至可能根本不会得到支持。

TypeScript 游乐场


推荐阅读