使用递归类型别名会产生循环引用错误
Posted
技术标签:
【中文标题】使用递归类型别名会产生循环引用错误【英文标题】: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
会引发循环引用错误,并将鼠标悬停在IntrospectionListTypeRef
或IntrospectionNonNullTypeRef
上会显示T extends any = any
,它确实不应该这样做。这里有什么问题吗?
这是TypeScript playground on v4.1.3的链接
请注意,此代码来自graphql
,我们目前正在处理migrating Flow to TypeScript。
这是Flow equivalent。
我发现的解决方法是:
-
将
any
传递给IntrospectionListTypeRef
。但这确实不是一个理想的解决方案。见GitHub diff
删除 IntrospectionListTypeRef
和 IntrospectionNonNullTypeRef
的泛型类型然后它会起作用,但问题是我们正在尝试移植更多类型,这意味着我们正在复制类型。
还提交了一个问题,请参阅TypeScript #42308
【问题讨论】:
【参考方案1】:TypeScript 不允许任意循环类型定义。如果我们查看microsoft/TypeScript#33050,它引入了对 TypeScript 3.7 循环类型引用的添加支持的拉取请求,它说:
此 PR 所做的具体更改是允许类型参数在以下类型的 aliased 类型中进行循环引用 [大致,在类型别名中]:
泛型类和接口类型的实例化(例如Array<Foo>
)。 数组类型(例如Foo[]
)。 元组类型(例如[string, Foo?]
)。
所以只有当Bar
是一个通用的class
或interface
时,它才支持像type Foo = Bar<Foo>
这样的东西。不支持 Bar
本身是 type
别名。
请参阅 a comment on the same pull request 来解释这一点。另请参阅 microsoft/TypeScript#35017,这是一个开放功能请求,旨在解除此限制。
然后,我能想到的最简单的解决方法是尽可能将您的每个 type
别名更改为 interface
s。在您的示例代码中,只有 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
【讨论】:
以上是关于使用递归类型别名会产生循环引用错误的主要内容,如果未能解决你的问题,请参考以下文章
函数高级类型名别名if-else 的使用包的使用for循环swich的使用数组的使用