TypeScript 3.5 和 3.6 之间的泛型参数推断不匹配

Mismatch in generic parameter inference between TypeScript 3.5 and 3.6

我正在尝试理解以下代码的类型检查:

const marker = Symbol();

type ConstItem = string | number | null;
type Item = ConstItem | { [index: string]: string };
type WrapOrConst<T extends Item> = Wrap<T> | (T extends ConstItem ? T : never);
type Wrap<T extends Item> = {
    __marker: typeof marker,
    __phantom: T;
}

declare function wrapped<T extends Item>(item: T): Wrap<T>;
declare function str(): string;

type UnionInner<T extends Array<WrapOrConst<Item>>> = T extends Array<WrapOrConst<infer U>> ? U : never;
declare function Union<T extends WrapOrConst<Item>[]>(...inner: T): Wrap<UnionInner<T>>;

const test = Union(wrapped(str()), null);
const test2: string | null = test.__phantom;

Playground

这在 TypeScript 3.6 中工作正常,但在 TypeScript 3.5 中失败,因为 test 被推断为 Wrap<Item> 而不是 Wrap<string | null>。我无法在发行说明中找到任何内容;这只是一个错误吗?我能以某种方式在早期版本(理想情况下,低至 TypeScript 3.1)中解决这个问题吗?

是的,它被认为是一个错误(microsoft/TypeScript#32434) whereby generic type parameter inference involving certain unions produced undesirable results. It was fixed (microsoft/TypeScript#32460) 对于 TypeScript 3.6,如您所见。

为了让您的代码在以前的版本中工作,您可能必须重构以在推理站点中涉及更少的显式联合,如下所示:

type Unwrap<U extends Item | Wrap<Item>> = U extends Wrap<infer I> ? I : U
declare function Union<T extends WrapOrConst<Item>[]>(...inner: T): Wrap<Unwrap<T[number]>>;

这似乎符合您的要求:

const test = Union(wrapped(str()), null).__phantom; // string | null

但我当然不知道您拥有的完整用例集,因此可能存在所需行为与上述行为不同的边缘情况。但这应该可以让您了解如何进行。

好的,希望对你有帮助;祝你好运!

Link to code