打字稿类型 联合泛型中的推理问题


type SingleOrArray<T> = T | [T]
function f<T extends 'a' | 'b'>(a: SingleOrArray<T>, b: SingleOrArray<T>) {
return a && b
}
f('a', ['b'])

然后我得到了一个错误:Argument of type '"a"' is not assignable to parameter of type 'SingleOrArray<"b">',而'f'被推断为:

function f<"b">(a: SingleOrArray<"b">, b: SingleOrArray<"b">): SingleOrArray<"b">"

我真的无法解释为什么,需要帮助!

如果这不能代表您尝试做的事情,那么这将没有多大帮助,但是在示例的函数中使用泛型是没有意义的。直接使用该类型,也许先为其别名:

type AOrB = 'a' | 'b';
function f(a: SingleOrArray<AOrB>, b: SingleOrArray<AOrB>) {
return a && b
}

这样,类型在两个参数中都是独立的。您可以在示例中通过引入另一个类型参数来解决此问题,然后将一个参数用于第一个参数,一个用于第二个参数。

function f<
T1 extends 'a' | 'b',
T2 extends 'a' | 'b'
>(a: SingleOrArray<T1>, b: SingleOrArray<T2>) {
return a && b
}

在深入研究源代码一整天后,我想我得到了答案:

事实上,<T>分别从T[T]推断为'a''b',这被称为候选类型。这两种候选类型具有不同的优先级:联合类型定义中的T具有特殊的优先级值1(查看 https://github.com/Microsoft/TypeScript/blob/master/src/compiler/types.ts,第 4398 行,NakedTypeVariable枚举值),而[T]具有公共优先级值0,这意味着优先级顺序更高。

然后,在比较类型详细信息之前,较高优先级的值0将立即覆盖1。这就是类型'a'被删除的原因。如果它们共享相同的优先级值,则它们将被合并(见下文)。

最简单的修复方法是删除<T extends 'a' | 'b'>。 没有任何断言,[T]中的T将在解构虚拟表达式['b']后推断为更广泛的类型string

我还发现了当存在多个候选类型时编译器是如何处理的:如果候选类型共享基本(如字符串、数字)协变类型,则使用联合符号|连接它们。否则将得到最左边的类型,而右边没有类型是超类型。

最新更新