Issue
As seen in this answer, Typescript 3.8 introduced:
import type
to safely import definitions (source):
import type only imports declarations to be used for type annotations and declarations. It always gets fully erased, so there’s no remnant of it at runtime. Similarly, export type only provides an export that can be used for type contexts, and is also erased from TypeScript’s output.
However, you still need to include the package as a dependency.
This can lead to circular dependencies as seen in my case. A simplified description of my monorepo
is:
client-web
: a web client powered byvite
(client)client-store
: a redux store package (model)image-gallery
: an image gallery package (presentation)
They all need to be aware of the following type:
type IImage = {
id: string;
title: string;
url: string;
urlThumb: string;
}
However, it is not clear to me where this type should "live". There seems to be a few options:
- put it in the presentation =>
image-gallery
and import it to the other packages - put it in the model =>
client-store
and import it to the other packages - create a shared
common-types
package (manually) - create an auto-generated shared
common-types
package (composition)
No matter which path you choose you may encounter the downsides of complicating your dependency graph and thus making your build sequential rather then parallel. Furthermore, not all types are made equal, sometimes you want your types close to a component, other times you want your types grouped by semantic context.
I wonder whether there is a way out I haven't thought of?
Solution
It's a common knowledge that circular dependency in javascript (and typescript) occurs when two modules import each other. What I want to clarify is that these two modules might not be directly dependent on each other. Example:
// A wants B from BC
A -> BC
// B wants A from A
BC -> A
Note that the actual dependencies aren't circular. The circular dependency happened only because of the file/module structure. In this case there is two solutions:
- Extract the common dependency in its own file.
- Merge the dependency cycle in one file.
In the previous example this corresponds to:
// 1.
A -> C
B -> A // No circular dependency
// 2.
ABC // No external dependency at all!
It appears to me that your IImage
is an independent unit that can be extracted into its own file/module. Depending on your monorepo management tool, you may extract it to its own file that lives on one of the projects. Remember, dependency cycle happens between files, not projects, so this solution is safe.
If your monorepo manager doesn't allow dependencies between non library modules, you have no option but to create a separate subproject.
In both cases (of the monorepo tool) I would suggest the latter approach as it's more future proof in a sense that it extracts common code in one subproject that wouldn't cause any dependency cycle in the future.
Answered By - Ahmed Shaqanbi
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.