基本上就是标题。我试图让 TS 限制作为参数传递的函数的返回类型,使其仅允许对象({x: 42}
),但它却允许任何东西。我已经绞尽脑汁一段时间了,所以我希望有人能告诉我为什么extends object
限制类型是不够的?
type ChainFunction<T, U extends object> = (value: T) => U | Promise<U>;
type Chainable<T> = {
to<U extends object>(fnOrObj: ChainFunction<T, U> | U): Chainable<T & U>;
};
declare function chain<T extends {}>(initialValue: T): Chainable<T>
chain({ a: 1 })
.to(({ a }) => 42) // this should complain that 42 isn't an object
我尝试过其他签名,例如:
type ChainFunction<T, U> = (value: T) => U extends object ? U | Promise<U> : never;
但我无法让任何事情发挥作用。
JavaScript/TypeScript 中的函数是可调用对象。它们可以具有与其他对象一样的属性,值得注意的是,它们是该
object
类型的子类型。因此在没有什么可以阻止
U
它成为函数类型。如果你有一个f
类型为 的函数(value: {a: number}) => number
,那么 callto(f)
将会成功,因为U
将被推断为(value: {a: number}) => number
。你可能希望U
应该是一个非函数对象,但你没有告诉 TypeScript 这一点。TypeScript 没有直接的方式来确切地说“不是函数”,因为 TypeScript 缺少否定类型。如果它有否定类型,也许你可以写
事情会突然按照你想要的方式运行。但是因为 TypeScript 没有这个功能,所以你必须近似它。有关完整讨论,请参阅Typescript 泛型类型(除函数外)。一种简单的方法是说 a
NonFunction
是一个没有属性的对象call
。所有函数都有一个call
属性,并且你关心的任何对象都不太可能具有这样的属性(对吗?你打算用作call
属性键吗?如果没有,这可能不会发生):一旦你这样做了,事情就会开始按预期进行:
所以这是一个相当简单的方法。当然,你可能会
{call: any}
在野外遇到这种情况,然后这将失败。你可以查看上面链接的 SO 问题,了解更复杂的方法来尝试仅拒绝函数。或者,如果您真的想禁止它们,您可以在泛型中使用条件类型,这样它始终是类型,并且您拒绝返回原语的函数,并计算from的类型参数,如下所示:
U
fnOrObj
Chainable
U
这完全符合您的要求,但可能有些过度。
游乐场链接到代码