closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

Type Only Import Reference

Open ChadKillingsworth opened this issue 8 years ago • 16 comments

A key component to avoiding circular dependencies is the option to reference a type without importing the module.

Example:

import ModelEvent from './model-event';

export default class Model extends EventTarget {
  changeItem(item) {
    this.item = item;
    this.dispatchEvent(new ModelEvent('change:'+item.name, this, item));
  }
}
import Model from './model';

export default class ModelEvent extends Event {
   /**
   * @param {string|!goog.events.EventId} type Event Type.
   * @param {!Model} model
   */
  constructor(type, model) {
  }
}

We need a way (without requiring closure-library) to import or reference a type from another module.

ChadKillingsworth avatar Apr 18 '16 20:04 ChadKillingsworth

Has there been any movement on this subject? I've been looking for a way to do exactly this, without much success.

stevethedev avatar May 19 '16 18:05 stevethedev

This is possible already. See https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/runtime_tests/module_tests/module_test_resources/referenceOtherModule.js

Needs to be documented better.

MatrixFrog avatar May 19 '16 18:05 MatrixFrog

@MatrixFrog It's nice that we have the runtime tests open sourced.

Dominator008 avatar May 19 '16 19:05 Dominator008

Yup. We don't have an easy way for non-Googlers to run them though :(

MatrixFrog avatar May 19 '16 19:05 MatrixFrog

@MatrixFrog Eventually we should switch the external build system to Bazel and use https://github.com/bazelbuild/rules_closure for them.

Dominator008 avatar May 19 '16 19:05 Dominator008

I'm sure I'm overlooking something simple, but is there a way to do this without requiring a parameter to be passed from one function to another? For example, if I were to deliberately setup circular factories:

// Left.js
export class Left {
    constructor() { console.log("You've created a left-hand node!"); }
    createRight() { return new Right(); }
}

... and ...

// Right.js
export class Right {
    constructor() { console.log("You've created a right-hand node!"); }
    createLeft() { return new Left(); }
}

I could import Left into Right, or vice-versa, but I can't import them both into each other. If they were defined in the same file, then variable hoisting would take care of the problem; but putting them in the same file isn't always a great solution.

In some languages (e.g. C) this could be resolved by creating a header file with declarations, and then a source file with definitions. Is there a similar behavior in Closure Compiler?

stevethedev avatar May 19 '16 20:05 stevethedev

To clarify, this bug covers the case when the module in question is included in the compilation unit, but we don't want to explicitly import it in our module.

There is another use case for type-only imports which we currently don't support, but we'd like to. One should be able to import some module only for types, and be sure that the code of that module won't be included in the output, even if it has side effects.

dimvar avatar May 19 '16 20:05 dimvar

Ah, ok. Your second paragraph was what I was hoping to do. Thank you all for your time!

stevethedev avatar May 19 '16 21:05 stevethedev

The test file is gone. Can anyone point to a solution?

CC @froxieye

arv avatar Jun 25 '18 20:06 arv

I think the test file might be gone because we un-implemented this feature? But that is only a very vague memory, I'm not sure.

MatrixFrog avatar Jun 25 '18 21:06 MatrixFrog

As far as syntax goes, FWIW the latest typescript added jsdoc support for this to do @param {!Model} model with @param {!import('./model')} model (since it's the default export in the given example, dot access available if not. Can also reference in a typedef if the type is needed throughout the file).

TypeScript 2.9: import types

brendankenny avatar Jun 25 '18 21:06 brendankenny

+1 for import('./module').Type like it's 2018 that means the JS files can also be transpiled OK.

zavr-1 avatar Nov 19 '18 21:11 zavr-1

@jplaisted can you comment on the current state of this?

Looks like code like this at least used to work as a way of referring to types in other files without importing them.

/**
 * @param {./exportClass.Parent} parent
 */
export default function(parent) {
  return parent.constructor.staticFunction();
}

The test file we had for it was module_test_resources/referenceOtherModule.js, but that got deleted over a year ago. @MatrixFrog says he thinks we unimplemented this feature.

brad4d avatar Nov 20 '18 22:11 brad4d

Created Google internal issue b/119831043

brad4d avatar Nov 20 '18 22:11 brad4d

@MatrixFrog is correct. It was not used, did not have robust testing, and used "." as a delimiter, so we removed it.

Using syntax based on dynamic import is a lot more idiomatic, though as I've discovered non-trivial to land due to our JSDoc parser. I still haven't had the time, and will bring this up again at future planning meanings.

jplaisted avatar Nov 21 '18 03:11 jplaisted

@jplaisted has there been any further discussion on this with all the other ES improvements made to the compiler? Or is there another recommended approach to type-only import references? Thank you!

schmidtk avatar May 12 '21 16:05 schmidtk