25

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?

4
  • Can't you just move class A to a different file, and import them both when you want to do b.print(...)? Commented Dec 5, 2016 at 20:42
  • or better yet, just make an interface for B that's placed in a different file and have both of the other files use that. Commented Dec 5, 2016 at 20:53
  • That's the very definition of a circular dependency. You just need to break that dependency... There's no reason why A needs to reference B. Move the rest of the code elsewhere. Commented Dec 5, 2016 at 20:54
  • use types if you are using typescript Commented Dec 5, 2016 at 21:01

4 Answers 4

30

This is now directly possible in TypeScript 2.9.

type MyType = import('mymodule').MyType;
const myValue: import('mymodule').MyType;
Sign up to request clarification or add additional context in comments.

1 Comment

This was perfect, thank you very much. I ended up doing mutation: keyof typeof import('./mutations'),
27

NOTE: This answer is outdated. The "import elision" feature described here was removed in TypeScript 4.9. Other, newer answers show better ways of doing type-only imports like import type {A} from 'a' or type A = import('a').A

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](http://www.typescriptlang.org/docs/handbook/modules.html#optional-module-loading-and-other-advanced-loading-scenarios):

> 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.

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.

4 Comments

Great answer! I have completely missed this, thanks for letting me know! You are correct in that my example was too simplified for my "real" problem.
What if the "type only" import has some side effects? Will the require statement be dropped? If so, how does TS determine this?
I don't think the compiler can determine if something has side effects or not. If a module has side effects, it's better to be explicit and use import for side-effects only form of import syntax, alongside with any named or default imports from the same module if there are any.
Odd I wonder if this changed, because I am seeing typescript emitting a js require which is never read right now.
16

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

Comments

-3

You used the [typescript] tag here, so I am going to assume you are using typescript.

Make this a 1-way street. Kill off the circular dependency. I would probably move this into 1 file, but you can still split it up like this.

a.ts (just an interface)

export interface A {
    name: string;
    new (name: string);
}

b.ts (implementation)

import { A } from 'a';

export class B implements A {
    /*...*/
}

1 Comment

I can surely move classes to other files (or do other refactoring) to fix the problem but that was not the question in this case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.