代码之家  ›  专栏  ›  技术社区  ›  Cerberus

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

  •  0
  • Cerberus  · 技术社区  · 5 年前

    我试图理解以下代码的类型检查:

    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>

    0 回复  |  直到 5 年前
        1
  •  1
  •   jcalz    5 年前

    是的,它被认为是一个错误( microsoft/TypeScript#32434 )由此,涉及某些并集的泛型类型参数推断产生了不希望的结果。是的 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