Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using `import type` statement to fix circular dependency reference error

Tags:

typescript

Let's say I have two files, A.js and B.js. Both need references to each other like this.

A.js

import { B } from "b"

export class A {
  constructor(public name: string) {}
}

let b = new B();
b.print(new A("This is a random name"));

B.js

import { A } from "a"

export class B {
  print(a: A) {
    console.log(a.name);
  }
}

The example above will create a circular reference which currently does not work in the JavaScript runtime I'm using. The file B.js really only need the type information, not the actual export object). I want the type from A.js to get static type checking. Is this possible?

like image 248
jnsmalm Avatar asked Dec 05 '16 20:12

jnsmalm


People also ask

How do you resolve circular dependency issues?

There are a couple of options to get rid of circular dependencies. For a longer chain, A -> B -> C -> D -> A , if one of the references is removed (for instance, the D -> A reference), the cyclic reference pattern is broken, as well. For simpler patterns, such as A -> B -> A , refactoring may be necessary.

What is circular import in Javascript?

A circular dependency is when one of your modules imports another modules, which directly or via other modules imports the first module. Examples: Direct reference: A -> B -> A. // a.js import { b } from './b.js' // b.js import { a } from './a.js'

What is circular dependency in typescript?

In software engineering, a circular dependency is a relation between two or more modules which either directly or indirectly depend on each other to function properly. Such modules are also known as mutually recursive.

How does node js handle circular dependency?

Circular dependencies are code smell. If A and B are always used together they are effectively a single module, so merge them. Or find a way of breaking the dependency; maybe its a composite pattern. make sure your necessary exports are defined before you require a file with a circular dependency.


3 Answers

This is now directly possible in TypeScript 2.9.

type MyType = import('mymodule').MyType;
const myValue: import('mymodule').MyType;
like image 87
Chris Groh Avatar answered Oct 11 '22 02:10

Chris Groh


UPDATE: this answer is likely to become obsolete as soon as TypeScript 3.8 is released

You don't need to do anything special to import only type information from module a.

Typescript will do it for you - if the only thing module b needs form a is type information, compiled file b.js will not have require("./a") statement, that is, it will not have runtime dependency on a. Quote from typescript handbook:

The compiler detects whether each module is used in the emitted JavaScript. If a module identifier is only ever used as part of a type annotations and never as an expression, then no require call is emitted for that module.

I just tried that:

file a.ts

import { B } from "./b"

export class A {
    constructor(public name: string) {}
}

let b = new B();
b.print(new A("This is a random name"));

file b.ts

import { A } from './a';

export class B {
    print(a: A) {
        console.log(a.name);
    }
}

compile them together

tsc a.ts b.ts

Here is resulting b.js:

"use strict";
var B = (function () {
    function B() {
    }
    B.prototype.print = function (a) {
        console.log(a.name);
    };
    return B;
}());
exports.B = B;

see? there is no require("./a"), unlike a.js which contains

var b_1 = require("./b");

Most likely, the example code you posted in your question is incomplete, and real b module has runtime dependency on a - find out where it is and get rid of that, and you won't have this problem.

like image 38
artem Avatar answered Oct 11 '22 00:10

artem


As of TypeScript 3.8, use import type:

import type { SomeThing } from "./some-module.js";

export type { SomeThing };

Reference:

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-exports

like image 7
Noah Stahl Avatar answered Oct 11 '22 01:10

Noah Stahl