为什么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% 替代品。