【问题标题】:Using recursive type aliases gives circular reference error使用递归类型别名会产生循环引用错误
【发布时间】:2021-04-17 23:43:32
【问题描述】:

根据TypeScript v3.7,递归类型别名可以使用。

export type IntrospectionType = {
  readonly kind: 'OBJECT';
};

export type IntrospectionListTypeRef<
  T extends IntrospectionTypeRef = IntrospectionTypeRef
> = {
  readonly kind: 'LIST';
  readonly ofType: T;
};

export type IntrospectionNonNullTypeRef<
  T extends IntrospectionTypeRef = IntrospectionTypeRef
> = {
  readonly kind: 'NON_NULL';
  readonly ofType: T;
};

export type IntrospectionTypeRef =
  | IntrospectionNamedTypeRef
  | IntrospectionListTypeRef
  | IntrospectionNonNullTypeRef<
      IntrospectionNamedTypeRef | IntrospectionListTypeRef
    >;

export type IntrospectionNamedTypeRef<
  T extends IntrospectionType = IntrospectionType
> = {
  readonly kind: T['kind'];
};
 

在这种情况下,IntrospectionTypeRef 会引发循环引用错误,并将鼠标悬停在IntrospectionListTypeRefIntrospectionNonNullTypeRef 上会显示T extends any = any,它确实不应该这样做。这里有什么问题吗?

这是TypeScript playground on v4.1.3的链接

请注意,此代码来自graphql,我们目前正在处理migrating Flow to TypeScript。 这是Flow equivalent

我发现的解决方法是:

  1. any 传递给IntrospectionListTypeRef。但这确实不是一个理想的解决方案。见GitHub diff
  2. 删除 IntrospectionListTypeRefIntrospectionNonNullTypeRef 的泛型类型然后它会起作用,但问题是我们正在尝试移植更多类型,这意味着我们正在复制类型。

还提交了一个问题,请参阅TypeScript #42308

【问题讨论】:

    标签: typescript graphql-js


    【解决方案1】:

    TypeScript 不允许任意循环类型定义。如果我们查看microsoft/TypeScript#33050,它引入了对 TypeScript 3.7 循环类型引用的添加支持的拉取请求,它说:

    此 PR 所做的具体更改是允许类型参数在以下类型的 aliased 类型中进行循环引用 [大致,在类型别名中]:

    • 泛型类和接口类型的实例化(例如Array&lt;Foo&gt;)。
    • 数组类型(例如Foo[])。
    • 元组类型(例如[string, Foo?])。

    所以只有当Bar 是一个通用的classinterface 时,它才支持像type Foo = Bar&lt;Foo&gt; 这样的东西。不支持 Bar 本身是 type 别名。

    请参阅 a comment on the same pull request 来解释这一点。另请参阅 microsoft/TypeScript#35017,这是一个开放功能请求,旨在解除此限制。


    然后,我能想到的最简单的解决方法是尽可能将您的每个 type 别名更改为 interfaces。在您的示例代码中,只有 IntrospectionTypeRef 本身需要保留 type 别名,因为它是联合类型。其他的都可以改:

    export interface IntrospectionType {
      readonly kind: 'OBJECT';
    };
    
    export interface IntrospectionListTypeRef<
      T extends IntrospectionTypeRef = IntrospectionTypeRef
      > {
      readonly kind: 'LIST';
      readonly ofType: T;
    };
    
    export interface IntrospectionNonNullTypeRef<
      T extends IntrospectionTypeRef = IntrospectionTypeRef
      > {
      readonly kind: 'NON_NULL';
      readonly ofType: T;
    };
    
    export type IntrospectionTypeRef =
      | IntrospectionNamedTypeRef
      | IntrospectionListTypeRef
      | IntrospectionNonNullTypeRef<
        IntrospectionNamedTypeRef | IntrospectionListTypeRef
      >;
    
    export interface IntrospectionNamedTypeRef<
      T extends IntrospectionType = IntrospectionType
      > {
      readonly kind: T['kind'];
    };
    
    export interface IntrospectionType {
      readonly kind: 'OBJECT';
    };
    
    export interface IntrospectionListTypeRef<
      T extends IntrospectionTypeRef = IntrospectionTypeRef
      > {
      readonly kind: 'LIST';
      readonly ofType: T;
    };
    
    export interface IntrospectionNonNullTypeRef<
      T extends IntrospectionTypeRef = IntrospectionTypeRef
      > {
      readonly kind: 'NON_NULL';
      readonly ofType: T;
    };
    
    export type IntrospectionTypeRef =
      | IntrospectionNamedTypeRef
      | IntrospectionListTypeRef
      | IntrospectionNonNullTypeRef<
        IntrospectionNamedTypeRef | IntrospectionListTypeRef
      >;
    
    export interface IntrospectionNamedTypeRef<
      T extends IntrospectionType = IntrospectionType
      > {
      readonly kind: T['kind'];
    };
    

    现在没有错误了,万岁!

    Playground link to code

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-09
      • 1970-01-01
      • 2018-07-08
      • 1970-01-01
      • 1970-01-01
      • 2018-12-09
      • 1970-01-01
      相关资源
      最近更新 更多