此泛型函数产生以下错误:类型(...arg: [TParams] | []) => void
不能分配给类型TParams extends undefined ? () => void : (params: TParams) => void
function useExample<
TParams extends object | undefined = undefined
>(): TParams extends undefined ? () => void : (params: TParams) => void {
const open = (...arg: [TParams] | []) => {};
return open;
^^^^ Type '(...arg: [TParams] | []) => void' is not assignable to type 'TParams extends undefined ? () => void : (params: TParams) => void'.
}
根据TParams泛型参数的不同,类型可以是() => void
或(params: TParams) => void
,所以在我看来,应该折叠为(...arg: [TParams] | []) => void
是合乎逻辑的。我错了/错过了什么吗?或者这是类型脚本目前无法处理的吗?
如果是这样,我如何创建一个有参数或不依赖于泛型参数的函数?示例:Playground
1条答案
按热度按时间wkyowqbh1#
请参阅microsoft/TypeScript#51488以获得此问题的规范答案。
让我们定义
这样你函数就属于
并且在下面的内容中,为了简洁,我们可以仅引用
F<T>
。你遇到的问题是编译器不认为
(...arg: [TParams] | []) => void
可以赋值给F<TParams>
,即使从F<T>
的定义来看,它可以赋值给条件的两边。当你有一个conditional type依赖于一个generic类型参数时,比如
F<T>
,编译器通常会 * 推迟 * 对它的求值,因此类型本质上是 * 不透明的 *。编译器并不会真正尝试查看什么值可以或不可以分配给这样的类型,至少直到类型参数实际上是用类型参数指定的。当条件类型是分布式的时尤其如此(其中所检查的类型是裸类型参数,例如T extends ⋯ ? ⋯ : ⋯
,而不是SomethingElse<T> extends ⋯ ? ⋯ : ⋯
)。分布条件类型分布在联合体之间,因此如果
G<T>
是分布条件类型,则G<A | B>
的计算结果将为G<A> | G<B>
。请注意,never
类型被视为 * 空联合体 *,因此无论如何G<never>
都将是never
(如果你意识到A | never
是A
,那么如果G<A | never>
分布到G<A> | G<never>
,那么对于所有可能的A
和分布G
,G<A>
总是G<A> | G<never>
,这是有意义的。所以G<never>
可能最好是never
)。由于您的类型F<T>
是分布式的,这意味着F<never>
是never
...而不是() => void
或(params: never) => void
。由于open
不是有效的F<never>
,从技术上讲,编译器抱怨赋值是正确的。即使不是这样,要弄清楚一个值是否可以为所有可能的union类型的T
赋值也太复杂了,编译器甚至都不去尝试。所以你得到一个错误。一般来说,当你有一个泛型条件类型,并且你需要给它赋值时,你可能只需要使用类型Assert,然后继续:
但是在这个特殊的例子中,你可能并不希望
F<T>
在T
中是分布式的(对吧?你不希望F<string | number>
是((params: string) => void) | ((params: number) => void)
,对吧?)所以你可以按照文档中关于分布式条件类型的说明来做:“为了避免这种行为,你可以用方括号将extends关键字的两边括起来。”(更多信息请参见How to avoid distributive conditional types)突然间一切都好了:
这是因为对于非分配的条件类型,编译器会检查值是否可分配给每一方,如果是,则允许分配。类型
F<TParams>
肯定会是() => void
或(params: TParams) => void
,并且由于(...arg: [TParams] | []) => void
可分配给这两者,因此分配被接受。因此,这可能是您在这个特定情况下想要做的,即使这个问题的一般版本(您可能需要分布式条件类型)在没有类型Assert之类的东西的情况下无法解决。
Playground链接到代码