泛型-使用参数函数的返回类型作为返回类型

时间:2020-07-23 01:47:15

标签: typescript

我有一个执行提取请求的功能。第一个参数描述请求。第二个参数是可选的转换函数:

let data = useData({ /* desc request */}, (res) => res.values)

transform函数在函数返回(即,设置data)之前转换响应。

这些功能的签名为:

// Signature for the transform function
type TransformFn<Data, R> = (data: Data) => R;

// Signature for the fetcher
function useData<Data = any>(
  firstArg: object,
): Data;
function useData<Data = any, R = any>(
  firstArg: object,
  fn?: TransformFn<Data, R>
): R;

如果我这样做,那么效果很好:

type Response = { values: string[] }

let data = useData<Response, Response["values"]>({ /* desc request */ }, (res) => res.values);

但是如果我不必传入第二种类型的参数(TransformedData),那就太神奇了。我希望能够键入transform函数的参数,然后让TypeScript的惊人推断从此处获取它,如下所示:

let data = useData<Response>({ /* desc request */ }, (res) => res.value);

就目前而言,data仅退回到默认类型arg any

有任何提示吗?谢谢。

2 个答案:

答案 0 :(得分:2)

不幸的是,TypeScript不支持部分类型参数推断(有关讨论,请参见microsoft/TypeScript#26242)。现在,它是全部还是什么都没有:要么让编译器推断所有类型参数,要么指定所有参数。 (似乎generic parameter defaults可以为您完成此操作,因为它可以让您仅指定一些参数;但是未指定的参数不是推断的,而是采用 default < / em>值。这不是您想要的。)

直到并且除非以某种方式实现了microsoft / TypeScript#26242,否则只有解决方法。我通常使用的两种解决方法是:


咖喱

由于不能同时在同一个泛型函数上指定和推断类型参数,因此可以将单个泛型函数拆分为多个函数,其中一个可以指定参数,另一个可以让编译器为您推断它们。例如:

declare function useDataCurry<D>():
    <R = D>(firstArg: object, fn?: TransformFn<D, R>) => R;

此处useDataCurry()是一个不带参数的函数,该函数返回另一个函数。可以在D上指定Data(您的useDataCurry())类型参数,并且可以在返回的第二个函数上推断出R类型参数。您可以这样使用它:

let data0 = useDataCurry<Response>()({ /*desc*/ }); // Response
let data1 = useDataCurry<Response>()({ /*desc*/ }, (res) => res.values); // string[]

这几乎就是您想要的, 除了其中有一个看起来很奇怪的中间函数调用。


假人

另一种方法是让编译器推断所有类型参数,但您传入的虚拟参数类型与您要指定的参数相对应。它甚至不必是该类型的真实值;您可以使用type assertion使null看起来像您想要的类型。

例如:

declare function useDataDummy<D, R = D>(
    dummyD: D, firstArg: object, fn?: TransformFn<D, R>): R;

在函数的实现中将忽略dummyD参数,但是它使编译器知道您想要的D类型:

let data2 = useDataDummy(null! as Response, { /*desc*/ }); // Response
let data3 = useDataDummy(null! as Response, { /*desc*/ }, res => res.values); // string[]

几乎也正是您想要的,除了您将类型参数指定为常规参数之外。哪个很奇怪,但是有效。


这两种方法都行得通,但都不是完美的。那好吧。无论如何,希望能有所帮助;祝你好运!

Playground link to code

答案 1 :(得分:1)

代替

function useData<Data = any, R = any>(
  firstArg: object,
  fn?: TransformFn<Data, R>
): R;

您可以使用fn类型的ReturnType utility type,看起来像这样

function useData<Data = any>(
  firstArg: object,
  fn?: TransformFn<Data, unknown>
): ReturnType<typeof fn>;

这将从签名中删除第二个泛型类型参数,与示例中的内容相匹配,并将useData的结果键入为返回类型fn