如果TypeScrip有在microsoft/TypeScript#29317中实现(但从未合并)的negated types种类型,那么您只需编写
// Don't do this, it isn't valid TypeScript:
declare function cloneAnythingButFunction<T extends not Function>(object: T): T;
然后就完事了.但在TypeScrip中没有not
,至少在TypeScrip4.8中是这样的,所以你不能.
存在try 模拟/仿真not
的各种方式.一种方法是做你正在做的事情:写一个conditional type,就像一个圆形generic constraint,你已经这样做了:
declare function cloneAnythingButFunction<T>(object: Exclude<T, Function>): T;
这适用于specific种类型的参数,例如
cloneAnythingButFunction(123); // okay
cloneAnythingButFunction({ a: 1, b: 2 }); // okay
cloneAnythingButFunction(Test) // error
cloneAnythingButFunction(() => 3) // error
cloneAnythingButFunction(new Test()); // okay
但当参数本身是generic类型时,它可能会崩溃.类方法中this
的polymorphic this
type是隐式泛型类型参数.(也就是说,它被视为类实例类型的某个未知类型constrained).编译器不知道如何验证this
到Exclude<this, Function>
的赋值,这是有意义的,因为编译器不知道如何说Test
的某个子类型可能不也实现Function
.
您可以通过将this
扩展为特定的超类型来解决此问题,如Test
:
class Test {
clone1() {
const thiz: Test = this;
return cloneAnythingButFunction(thiz); // okay
// return type is Test, not this
}
}
另一种近似否定类型的方法是将所有可能类型的集合分割成mostly个覆盖要否定的事物的补码的部分.我们知道没有基元类型是函数,所以我们可以从
type NotFunction = string | number | boolean | null | undefined | bigint | ....
然后我们可以开始添加同样不是函数的对象类型.可能任何类似数组的类型也不是函数:
type NotFunction = string | number | boolean | null | undefined | bigint |
readonly any[] | ...
任何没有定义的apply
属性的对象也不是函数:
type NotFunction = string | number | boolean | null | undefined | bigint |
readonly any[] | { apply?: never, [k: string]: any } | ...
任何没有定义的call
属性的对象也不是函数:
type NotFunction = string | number | boolean | null | undefined | bigint |
readonly any[] | { apply?: never, [k: string]: any } |
{ call?: never, [k: string]: any };
我们应该继续前进,还是停在那里?上述NotFunction
的定义将对同时具有apply
属性和call
属性的任何对象进行错误分类.我们担心这些吗?我们可能会遇到具有名为apply
和call
的属性的非函数对象吗?如果是这样的话,我们可以增加更多的片段.也许我们想添加没有bind
属性的对象.或具有bind
、call
和apply
属性的对象,但其中每个属性本身都是原语,如{ call: string | number | ... , apply: string | number | ... }
……但在某种程度上,我们应该停下来.对于许多用例来说,将????近似为3.14已经足够好了,而将其近似为3.141592653589793238462643383通常会带来更多麻烦.让我们只使用上面的定义.
不管怎样,现在让我们试着用NotFunction
代替not Function
:
declare function cloneAnythingButFunction<T extends NotFunction>(object: T): T;
cloneAnythingButFunction(123); // okay
cloneAnythingButFunction({ a: 1, b: 2 });
cloneAnythingButFunction(Test) // error
cloneAnythingButFunction(() => 3) // error
cloneAnythingButFunction(new Test()); // okay
class Test {
clone1() {
return cloneAnythingButFunction(this); // okay
}
}
它们的行为符合人们的期望.仍然是possible,Test
的某个子类型可以赋值给Function
,但是编译器不在乎,我想我们也不在乎.
当然,我们并不关心这个:
cloneAnythingButFunction({apply: "today", call: "1-800-TYP-SCRP"}); // error oh noez
因为如果我们这样做了,我们将不得不在我们的大约NotFunction
的基础上增加更多的????数字来处理它.
Playground link to code个