Import shared/partial module into another file - javascript

Say I have two files:
box.ts:
export module Entities {
export class Box { .. Stuff }
origin.ts:
export module Entities {
export class Origin { .. other stuff }
As I understand javascript, those two modules should merge into one module that has both my classes in it.. (I just want them in separate files for organizational reasons, but both box and origin are entities.)
So now I have a file that needs to use both of those:
service-actions.ts:
import {Entities} from '../entities/box';
import {Entities} from '../entities/origin';
... stuff that uses both of those entities.
This gives me the error:
Duplicate identifier 'Entities';
If I have one or the other, it works fine, but both fail.
Is there a way I can combine these so that the I can do Entities.Box and Entities.Origin in my service-action.ts file?
Is the only way to do this is to put Box and Origin in the same file?

Don't mix namespaces (internal modules) with external modules. See this post and this question and others for arguments against mixing. I've found it best to not use internal modules altogether. We don't gain any encapsulation benefits from namespaces. Instead, directories are a simple and solid way to organize code, and you are using them already.
entities/box.ts:
export class Box { .. Stuff }
entities/origin.ts:
export class Origin { .. other stuff }
Now instead of having to write Entities.Box, you can just import Box and use it:
import { Box } from '../entities/box';
import { Origin } from '../entities/origin';

Use as:
import {Entities as BoxEntities} from './../entities/box';
import {Entities as OriginEntities} from './../entities/origin';
// Use BoxEntities and OriginEntities here
Preferably, if you are actually exporting the classes directly from the files as you should be (and as pointed out by mk.), you could create an entities.ts file that handles it for you:
export * from './entities/box';
export * from './entities/origin';
Then import this file:
import * as Entities from './../entities';
// use Entities.Box and Entities.Origin here

Related

ClassName/ClassName.js Module Resolution with Webpack & Node.js

Often times I see the following directory structure and import pattern in Javascript projects:
ClassName/index.js pattern
RepoRoot
|-src
|-index.js
|-ClassName
|-index.js
|-SupportingClass.js
src/ClassName/index.js
import { SupportingClass } from './SupportingClass';
export class ClassName {
// Implementation
}
src/index.js
import { ClassName } from './ClassName';
This is all fine and dandy. It allows for a much cleaner import of the ClassName class. However I'm not a fan of the name of the class file index.js not having the same name as the class's name. It:
Makes it harder to locate the file in a file name search (Command+P in VS Code)
You end up with just a very unhelpful index.js in the file tab in the IDE (yes I know some IDEs are smart enough to add a bit more context when you have more than one file of the same name open. That helps, but isn't a real solution imo)
Other files that aren't the indexes get much more descriptive names: SupportingClass.js, someUtils.js, etc.
ClassName/ClassName.js pattern
RepoRoot
|-src
|-index.js
|-ClassName
|-ClassName.js
|-SupportingClass.js
src/ClassName/ClassName.js
import { SupportingClass } from './SupportingClass';
export class ClassName {
// Implementation
}
src/index.js
import { ClassName } from './ClassName/ClassName';
This solves all of the problems I laid out, but makes the import of ClassName a bit duplicative. Probably not a big deal for most people especially with your IDE being able to auto import things for you nowadays.
However...
Just how webpack & node.js understand out of the box how to interpret the import path:
./ClassName as ./ClassName/index.js
Is there a setting or plugin that allows for it to interpret:
./ClassName as ./ClassName/ClassName.js?
I've been wondering for years if such a thing existed but could never really find any literature on it. Curious if anyone here knows of anything.
You are dealing with named imports and default imports, more details on this discussion can be found here:
How to import both default and named from an ES6 module
From your situation (I haven't tried with subfolders, but it should work), you may want to organize your files as such:
RepoRoot
|-src
|-index.js
|-ClassName
|-ClassName.js (named module)
|-SupportingClass.js (named module)
|-index.js (default modules)
And then you can easily import your stuff in src/index.js such as:
import ClassName, SupportingClass, { otherValues } from './ClassName'

Exporting one set of functions from multiple files

I've just finished writing a big quickbooks wrapper in JavaScript with a lot of parts in different files, e.g. a file for tax, a file for accounts, a file for auth, etc. I'd like to make it so that developers using this wrapper will only have to do one import that will contain all the functions I've written.
Hierarchy is something like this:
QbTax.js
QbAccounts.js
QbAuth.js
I'd like to have another file, or a way that a developer using this wrapper would only have to import one thing, then be able to call all functions from the above files from that one import.
Something like this:
import * as qb from './unifiedQbFile.js';
qb.thisIsATaxFunc();
qb.thisIsAnAccountsFunc();
qb.thisIsAnAuthFunc();
What is the best way to approach this?
The only idea I have at the moment is to write prototypes in a file (unifiedQbFile.js for instance) and export those. I'd import all the functions from my other files in that unified file then call them in my new prototypes. That seems messy though.
you can have one index.js file that exports all files then you can import that index.js file. I would personally go with this method.
// index.js
export { qbTaxFunc } from 'QBTax';
export { qbAccountsFunc } from 'QbAccounts';
export { qbAuthFunc } from 'QbAuth';
Now in some other script to import it you can do.
import qb from 'path-to-index.js';
// All functions should now be available
qb.qbTaxFunc();
qb.qbAccountsFunc();
qb.qbAuthFunc();

How to get around airbnb eslint import/prefer-default-export rule when using index.js for exports

For cases like this,
/ACollectionOfTinyComponent/index.js
import Container from './Container';
export {
Container,
};
So the index.js becomes a directory to include other small parts without having to write each Component Name out all the time.
So in this case, we can import a component in another component like following:
import {Container} from './ACollectionOfTinyComponent'
//then use Container in the code here
Is this a bad practice? Since if I have airbnb linter enable then I got error linting of
Prefer default export import/prefer-default-export
it asks me to add default but that will cause compile error
I found out that because I added only ONE import & export for the index.js. But if I add more than one its fine!
For example, if I do
import Container from './Container';
import Ezeewei from './Ezeewei';
export {
Container,
Ezeewei,
};
Note that I added one more import of Ezeewei.
Then the linting rule will pass!
it asks me to add default but that will cause compile error
You must be using the wrong syntax for exporting a default.
export { name1 as default, … };
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Syntax
import {Container} ... Is this a bad practice?
Yes. If you have a single entry point into a component (like a a class), you should export it as the default rather than a named export. The designers of the language gave default imports and exports a special syntax to promote it as the primary use case.
http://2ality.com/2014/09/es6-modules-final.html#default-exports-are-favored

Dynamically reference static ESNext imports

Say I have these imports:
import clearLineReporter from '../modules/clear-line-reporter';
import karmaReporter from '../modules/karma-reporter';
import metaTestReporter from '../modules/meta-test-reporter';
import stdReporter from '../modules/std-reporter';
import tapJSONReporter from '../modules/tap-json-reporter';
import tapReporter from '../modules/tap-reporter';
import webSocketReporter from '../modules/websocket-reporter';
these must be referenced like I do above, in other words, I obviously can't do this:
const imports = {
stdReporter: import(...),
tapJSONReporter: import(...),
...
webSocketReporter: import(...)
}
Is there any way I can reference imported files through some form of reflection? Because it seems like I can't group them together to reference them somehow.
Instead of import syntax, I could use require(), but I am wondering if there is some way I can do some dynamic things with import statements, for example reference them all dynamically, such that if I add or remove an import, I don't have to change any other code.
There is a great answer to this question that I discovered by asking a different question, here:
exporting imports as a namespace with TypeScript
Create a file name grouped-modules.ts for example, where you want to simply list only the modules and export each one.
export {default as clearLineReporter} from '../modules/clear-line-reporter';
export {default as karmaReporter} from '../modules/karma-reporter';
export {default as metaTestReporter} from '../modules/meta-test-reporter';
...
export {default as stdReporter} from '../modules/std-reporter';
export {default as tapJSONReporter} from '../modules/tap-json-reporter';
Then in your module you can just do :
import * as mods from './grouped-modules'
export {mods}
It will export both types and values in a namespace called s. You can then import them using :
import {mods} from 'your-module'
const anObject: mods.clearLineReporter = ...;
This allows you to dynamically group your modules into one variable.
Is there any way I can reference imported files through some form of reflection?
Answer is dependent on environment, meant in questing, because import statement can be ES native modules implementation in browser, or babel-ed to require statements in node.js, or compile-time resolved bindings in webpack.
So, in each case there is solution to do something reflection. In node.js with babel-ed code import is just require wrapper, so any information is available there.
In browser with native ES modules, all requests to them can be served via ServiceWorker, so it can provide necessary information about fetched ES modules. Also in browser ES modules can be dynamically imported that way: https://matthewphillips.info/posts/loading-app-with-script-module
Most interesting part is webpack: compile-time resolve and semi-reflection can be achieved by externals resolver in functional style (https://webpack.js.org/configuration/externals/#function), and runtime by load module API (https://webpack.js.org/api/module-variables/#webpack_modules-webpack-specific- )

Should I use both `import 'rxjs/Rx'` and `import { Observable } from '#rxjs/Observable'`

import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Observable } from '#rxjs/Observable';
import 'rxjs/Rx';
import 'rxjs/add/observable/throw';
#Component({});
export shellModule{}
This is a piece of code form my Angular app that I copied from somewhere (I have removed the definitions in the exported module. I am using it to make a service to call APIs.
In the imports in this particular file, why is it that Observable is imported separately even though the entire rxjshas been imported. If a particular module is being imported in its entirety, why is a particular object from it imported separately? I tried asking this question at the forum from where I took it, but there was no answer. I want to understand if this somehow helps with optimization of code.
In general:
In Typescript, the way modules are handled would require you to either load in the entire library with the import * as rx from 'rxjs/Rx', or a specific exported module within the library to use it, so the the compiler loads in the types.
Reducing your imports to only the specific modules you need sets up your app to use tree shaking from Angular's AOT compilation. This is not done by the typescript compiler, but by a tool called rollup. So, it can help with optimizing code later, but it doesn't automatically do so.
As far as compilation overhead, bringing in the whole library might slow down the compiler a bit... but this isn't a very strong point except for massively complex libraries.
I, personally, prefer importing in specific modules because it makes the calling code a little cleaner since I don't need to use that global name to get to the specific name. rx.Observable vs Observable. A good example of this is the lodash library (rxjs is a bit more complex...)
Honestly, importing entire libraries like the line you have there: import 'rxjs/Rx' doesn't make sense to me. You should only import specific exported modules. Try removing it, seeing what errors you get, and then using the * as rx syntax instead.
As far as rxjs goes - it is a little wonky when you want to import specific operators like this question does - so the way to get specific operators is with: import 'rxjs/add/observable/from' - but that also requires a tinkering with your webpack set up as outlined in the referenced question's answer.
Let's see what the rxjs/Rx module exports:
export { Subject, AnonymousSubject } from './Subject';
export { Observable } from './Observable';
export { Operator } from './Operator';
export { Observer } from './Observer';
export { Subscription } from './Subscription';
export { Subscriber } from './Subscriber';
export { AsyncSubject } from './AsyncSubject';
export { ReplaySubject } from './ReplaySubject';
export { BehaviorSubject } from './BehaviorSubject';
...
import './add/observable/bindCallback';
import './add/observable/bindNodeCallback';
import './add/observable/combineLatest';
...
So it exports RxJs classes and also imports operators from the add folder. So as you can see it loads everything in the library. It doesn't export any global object though. So you need to use named export like this:
import * as Rx from 'rxjs/Rx'
to be able to use an exported class:
Rx.Observable.of(12, 3);
This emulates what you would have if you loaded the library using the bundle - a global Rx object:
<script src="rxjs/bundles/Rx.js">
If you want to use Observable without Rx global object, you need to import it separately:
import { Observable } from '#rxjs/Observable';
Observable.of(1);
Importing both
import { Observable } from '#rxjs/Observable';
import 'rxjs/Rx';
is not a good practice, but may be used if you don't want to import every operator separately.
Also see How to correctly import operators from the rxjs package.

Categories

Resources