【问题标题】:How to differentiate between value and callback argument type如何区分值和回调参数类型
【发布时间】:2020-05-14 15:18:41
【问题描述】:

这个sn-p

function problem<T>(callback: T | (() => T)) : T {
    return typeof callback === 'function' ? callback() : callback;
}

产生错误

This expression is not callable.
  Not all constituents of type '(() => T) | (T & Function)' are callable.
    Type 'T & Function' has no call signatures. ts(2349)

我花了一段时间才明白Function 本身确实没有多大意义,因为我需要的是一个无参数函数。

在像T = number 这样的特殊情况下,我可以简单地切换到测试typeof callback === 'number' 并完成。但是,我需要一个通用的解决方案。

我愿意

  • 要么限制T,使其不包含函数,这(如我所愿)解决了问题。
  • 或对无参数函数进行运行时测试

我也对替代方案持开放态度(我的主要目的是了解细节)。

有哪些可能性?

问题说明

function myfun(s: string) {return s;} 和电话

problem(myfun);

类型T = (s: string) =&gt; string 被正确推断。应该没有像myfun()这样的回调调用;相反,应该返回myfun。但是,typeof callback === 'function' 成立时出错了。

更新

假设 检查 callback instanceof Function 与 AFAIK 完全相同,我错了。

这是不同的,正如playground from the answer 所示。但是,添加

const myfun = (s: string) => s;
console.log(problem(myfun));

说出来

Argument of type '(s: string) => string' is not assignable to parameter of type 'string | (() => string)'.
  Type '(s: string) => string' is not assignable to type '() => string'.ts(2345)
doAfter.ts(57, 21): Did you mean to call this expression?

这听起来像这行的错误

console.log(problem<(s: string) => string>(myfun));

编译并且没有类型歧义。但是,它不起作用,返回的是 undefined 而不是 myfun 本身。

潜在的重复

链接问题的答案也不能解决我的问题:

  • 普通的correct(myfun); 无法编译。
  • 有效correct&lt;(s: string) =&gt; string&gt;(myfun)返回未定义。

我的第二个问题“(如何)对无参数函数进行运行时测试”也没有出现在潜在的重复项中。

【问题讨论】:

  • 如果您对运行时的这种行为感到满意,您可以使用type predicate 向编译器解释:typescriptlang.org/play/#code/…
  • 改用callback instanceof Function ? callback() : callback怎么样?
  • @jonrsharpe 这不是问题所在:Typescript 确实 理解 callback 是一个函数。但是,它说它缺少调用签名,这是正确的:请参阅我的编辑。
  • @CRice 我敢打赌,没有任何变化。
  • 这能回答你的问题吗? Typescript type T or function () => T usage

标签: typescript callback overloading


【解决方案1】:

我明白你现在要做什么了。

目标:如果给定的回调不是无参数的,那么它应该被推断为T类型并直接返回。如果它是无参数的,它应该被推断为() =&gt; T 并被调用。为此,我建议使用overload signature,如下所示:

function problem<T extends () => any>(callback: T): ReturnType<T>;
function problem<T>(callback: T): T;
function problem(callback: unknown): unknown {
    /* ... */
}

那么,当使用它时,会推断出以下类型:

const test0 = problem("") // literal type: ""
const test1 = problem(() => "") // string
const test2 = problem((s: string) => s) // (s: string) => string

最后,您需要更改problem 实现中的检查,以便它可以确定给定的回调是否是一个函数以及它是否接受参数。值得庆幸的是,在 javascript 中,函数有一个 .length 属性,指示所需参数的数量(带有可变参数函数和默认参数的警告)。所以:

return callback instanceof Function && !callback.length ? callback() : callback;

应该可以。


Playground Link.

【讨论】:

  • 完美!虽然我不再相信这样的功能是个好主意,但我学到的东西肯定是有用的。
【解决方案2】:

通过instanceof检查就足够了:

function problem<T>(callback: T | ((...args: any[]) => T)): T {
  return callback instanceof Function ? callback() : callback;
}

const functionReturnsFromParameter = (s: string) => s;
const callbackReturnsString = () => 'Hello, World from function!';
const justAString = 'Hello, World from constant!'

console.log(problem(functionReturnsFromParameter))
console.log(problem(callbackReturnsString));
console.log(problem(justAString));

Playground

【讨论】:

  • 这不适用于const myfun = (s: string) =&gt; s;。当使用problem&lt;(s: string) =&gt; string&gt;(myfun) 之类的类型参数时,它确实会返回错误的结果(即未定义)。
  • 它返回 undefined 因为你传递了一个函数但没有提供参数。不过,它是一个可以调用的函数。这是预期的行为。
  • 我已经更新了代码,因此它可以使用您的myfun 进行编译,但是再次......您没有使用字符串参数调用该函数,那么它应该从哪里获取您的字符串呢?
  • 它不应该接受任何字符串,而是按原样返回函数。重载的原因是为了简化使用:您要么传递一个值本身,要么传递一个提供该值的无参数函数(这意味着该值不能是一个无参数函数,但这没关系)。对于myfun,它应该被视为一个值。
  • 那你为什么要调用这个函数呢?根据您的措辞,您只需在两种情况下都按原样返回值,无论它是否是函数,您只需返回传递的内容。
猜你喜欢
  • 1970-01-01
  • 2018-08-04
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多