【问题标题】:How to solve Circular Dependencies when useing classes as Types in Typescript?在 Typescript 中使用类作为类型时如何解决循环依赖?
【发布时间】:2021-07-16 14:46:35
【问题描述】:

我如何使用不会遇到循环依赖错误的打字稿?

似乎发生了循环依赖错误,即使在代码编译为有效 JS 时应该删除导入。这是一个错误吗?

用户模型.ts

import { Post } from '../post-model'

export class User {
  Posts: Post[];
}

post-model.ts

import { User } from '../user-model'

export class Post {
  User: User;
}

我听说过两种可能的解决方案都不能满足我的要求。

一个是,创建一个与类匹配的新接口: Circular dependency caused by importing typescript type

我在 typegraphql 的文档中读过一些东西: https://typegraphql.com/docs/types-and-fields.html

他们说:

为什么使用函数语法而不是简单的 { type: Rate } 配置对象?因为,通过使用函数语法,我们解决了循环依赖的问题(例如 Post User),所以它被用作约定。如果您想节省一些击键,则可以使用简写语法 @Field(() => Rate),但对其他人来说可能不太可读。

我也没有找到任何选项来禁用打字稿中的循环依赖警告。

我在 Nrwl/Angular 9.x 中工作

提前感谢您的帮助!

【问题讨论】:

  • 这是无效的。你不能有这样的圆形类型。说一个用户有一个帖子列表,每个用户都有一个用户,每个用户都有一个帖子列表,每个用户都有一个用户,每个用户都有一个帖子列表,这在逻辑上是不合理的......和以此类推,无止境。这可能是真的,但它不是你可以编码的东西
  • 但它有效。这就是为什么如果将两个类放在同一个文件中它会起作用的原因。这里的循环问题不是关于类,而是关于导入。导入告诉 TS 应该首先编译哪些文件,从而产生了这个问题。
  • @bryan60 抱歉,这种循环关系是完全有效的。有很多 ORM 使用它。
  • 谢谢大家。这是一个令人困惑的问题。在我看来,Javascript 可能是完全有效的,但 Typescript 似乎需要找到正确的顺序来编译这些文件。我想如果打字稿编译器可以区分用于打字的导入和用作实际值的导入,那会很好。这样,我的代码就不会有循环依赖了。
  • Typescript 试图解决的实际问题是在发生转译之后,哪些文件内容将首先物理放置在生成的 Javascript 中。在您的情况下,所有内容都封装在一个类中,但想象一下该文件中是否还有全局 javascript 引用。这个顺序很重要。所以它会抛出一个关于循环引用的错误来规避这个。

标签: typescript typescript-typings circular-dependency typegraphql


【解决方案1】:

另一个不使用接口的解决方案是使用type only imports

user-model.ts

import type { Post } from './post-model'

export class User {
  Posts: Post[];
}

user-model.ts

import type { User } from './user-model'

export class Post {
  User: User;
}

这些导入在编译时会被完全删除,并且仅用于类型检查 - 这意味着您不能将它们用作值(例如,您不能使用仅类型导入来执行 new Post())。

我认为这种方法比只为了类型检查而创建带有接口的单独文件的替代方法更干净、更干。

【讨论】:

  • 太棒了!这就是我要找的!是新功能吗?
  • @DevJules 似乎是在 2020 年 2 月 20 日发布的 typescript 3.8 中引入的,所以当您提出原始问题时,它已经发布了 2 个月。
【解决方案2】:

使用接口是这里的最佳选择。您可以为每个创建一个.d.ts,然后将其导入。

user.d.ts

export interface IUser {
  Posts: IPost[];
}

post.d.ts

export interface IPost {
  User: IUser;
}

然后……

import { IPost, IUser } from './post.d'

export class User implements IUser {
  Posts: IPost[];
}

【讨论】:

  • 这个答案应该有更多的赞成票。我只是用它来修复我项目中的一堆错误。很酷的模式,只需要进行一些设置即可实现。
  • 对我来说,如果您使用更大的类和更多相互依赖的模型,这种方法似乎很难维护。您始终需要使您的接口保持最新并实现方法和返回类型。接口不会导致循环依赖,但如果类只是用作接口之类的类型,为什么会如此。
【解决方案3】:

只有将它们放在同一个文件中才能这样做

export class User {
  Posts: Post[];
}

export class Post {
  User: User;
}

或者如果你写类型

user-post.types.ts

export interface User {
  Posts: Post[];
}

export interface Post {
  User: User;
}

user-model.ts

import { Post } from '../user-post.types'

export class User {
  Posts: Post[];
}

post-model.ts

import { User } from '../user-post.types'

export class Post {
  User: User;
}

【讨论】:

    猜你喜欢
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多