【问题标题】:Typescript: change function type so that it returns new value打字稿:更改函数类型以使其返回新值
【发布时间】:2018-10-05 07:14:09
【问题描述】:

基本上,我想要这样的东西:

export type ReturnValueMapper<Func extends (...args: Args[] /* impossible */ ) => any, ReturnValue> = (...args: Args[]) => ReturnValue;

我几乎可以肯定这是不可能的,但我还没有找到确切的确认。


该用例正在改进 recompose's withStateHandlers 的类型,从而可以像这样定义状态更新器:

interface StateUpdaters {
    update(field: string): void; // I don't want to specify Partial<State> here 
}

【问题讨论】:

  • 我相信你可以在 2.8 中做到这一点
  • 这个问题并不完全是重复的,但请查看this answer
  • TS 2.8 允许您使用ReturnType&lt;T&gt; 提取函数的返回类型,但我认为映射您需要的函数签名variadic types,尚未实现。
  • @Aaron 如果你想要一个完美的解决方案,你可以这样做,对于一个可行的解决方案,@jcalz 引用的答案正是这样做的,使用条件类型ReplaceReturnTypein 该答案替换了返回类型。

标签: typescript recompose


【解决方案1】:

编辑

自从回答了最初的问题以来,打字稿已经改进了这个问题的可能解决方案。加上Tuples in rest parameters and spread expressions,我们现在不需要所有的重载:

type ReplaceReturnType<T extends (...a: any) => any, TNewReturn> = (...a: Parameters<T>) => TNewReturn;

这不仅更短,而且解决了许多问题

  • 可选参数仍然是可选的
  • 参数名称被保留
  • 适用于任意数量的参数

示例:

type WithOptional = ReplaceReturnType<(n?: number)=> string, Promise<string>>;
let x!: WithOptional; // Typed as (n?: number) => Promise<string>
x() // Valid
x(1); //Ok

原创


要获得好的解决方案,您需要variadic types,但目前这个answer 提供了一个可行的解决方案。 (将其发布在此处,因为该类型用作解决不同问题的一部分)。

基本思想是我们将提取参数类型并用新的返回类型重构函数签名。这种方法有几个缺点:

  1. 不保留参数名称
  2. 可选参数处理不好
  3. 仅适用于特定数量的参数(但可以根据需要添加更多参数)

可能还有其他问题,但根据您的用例,在类型系统解决此用例之前,这可能是一个足够好的解决方案。

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type ReplaceReturnType<T, TNewReturn> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
    IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewReturn :
    IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewReturn :
    IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewReturn :
    IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewReturn :
    IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => TNewReturn :
    IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => TNewReturn :
    IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => TNewReturn :
    IsValidArg<C> extends true ? (a: A, b: B, c: C) => TNewReturn :
    IsValidArg<B> extends true ? (a: A, b: B) => TNewReturn :
    IsValidArg<A> extends true ? (a: A) => TNewReturn :
    () => TNewReturn
) : never

与可选参数一起使用时的问题是可选参数成为必需的(并且是type A | undefined):

type WithOptional = ReplaceReturnType<(n?: number)=> string, Promise<string>>;

let x!: WithOptional;
x(); //invalid
x(undefined);
x(1);

【讨论】:

  • 这似乎正是我所需要的,谢谢。但是,我不确定“可选参数处理得不好”是什么意思,因为我可以使用它们并且看不出它们为什么会损坏。你能举个例子吗?
  • @LeonidFenko 添加了对我的神秘警告的解释 :)
  • 哦,我明白了。就我而言,我没有注意到它,因为我实际上从未调用过修改类型的函数。所以这对我来说是一个完美的解决方案。
  • 最新的更新似乎没有保留 v3.0.1 中的参数类型。它总是以 (...args: {}) =&gt; TNewReturn 结尾,并且使用它来键入函数将允许 args 的任意类型。
  • Typescript 现在带有Parameters,它的作用与ArgumentTypes 在答案中所做的一样
猜你喜欢
  • 2022-01-12
  • 2015-08-10
  • 2019-03-20
  • 2021-09-17
  • 1970-01-01
  • 2022-07-07
  • 2019-07-06
  • 2020-05-09
  • 2021-06-17
相关资源
最近更新 更多