给定类型 "anything except function OR function",我如何检查它是否是 TypeScript 中的函数?

Given the type "anything except function OR function", how do I check if it's a function in TypeScript?

为什么 valueif (typeof value === 'function') 检查后不是 () => string 类型?

应该进行什么检查,以便正确推断类型(使强制转换变得不必要)?

function foo<T extends {}>(value: Exclude<T, Function> | (() => string)) {
    let result: string;

    if (typeof value === 'function') {
        result = value(); // Error: Type '(() => string) | (Exclude<T, Function> & Function)' has no compatible call signatures.
        result = (value as () => string)(); // works, but I don't like it
    } else {
        result = value.toString();
    }

    console.log(result);
}

问题是 typescript 无法对条件类型进行大量推理,而其中仍有未解析的参数(例如函数内部的 T)。因此,当遇到类型保护时,typescript 只会将参数类型与 Function 相交,导致 (() => string) | (Exclude<T, Function> & Function) 仍然不可调用。

到目前为止,解决这个问题的最简单方法是使用类型断言。

你也可以用不同的方式表达条件。一个函数必须有一个 call 方法。如果我们将 T 约束为,如果它有一个 call 成员,它是与函数中的 call 不兼容的类型,我们实际上将 T 排除在外功能:

function foo<T extends number | string | boolean | null | undefined | ({ call?: undefined; [k: string]: any }) = never>(value: T | (() => string)) {
    let result: string;

    if (typeof value === 'function') {
        result = value(); // ok 
    } else {
        result = value.toString();
    }

    console.log(result);
}

foo(1)
foo({
    o: ""
})
foo(() => "");
foo(() => 1); // error

也许新的 negated types features 会提高 typescript 在涉及类型参数时进行推理的能力。虽然 Exclude 可以很好地过滤类型,但它不能 100% 替代否定类型。