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



为什么if (typeof value === 'function')检查后value不是() => 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

也许新的否定类型功能将提高 typescript 在涉及类型参数时进行推理的能力。虽然Exclude在筛选类型方面做得很好,但它并不是否定类型的 100% 替代品。

最新更新