QUESTION
There are a number of posts about this topic, but I'm still unsure as to the best practice for JS imports.
STYLE GUIDES CONFLICT
Google Style Guide Google Import Style Guide suggest the following approach is "good"
import * as goog from '../closure/goog/goog.js';
AirBNB Style Guide AirBNB Style Guide clearly states not use wildcards.
Observation
As I look through some respected JS developers I see them use wildcards. Originally I suspected it was for namespace or aliasing purposes, but I've seen this type of example more often than not.
import * as debounce from 'lodash/debounce.js'
The only reason I can imagine is maintainability i.e. debounce will remain debounce independent of export, and in this cases debounce will debounce as long as a debounce.js file exists.
The difference between
OPTION 1:
import debounce from "lodash/debounce.js";
OR
OPTION 2:
import * as debounce from 'lodash/debounce.js'
Is that option 1 seems to generate (example only)
var _debounce = require('./lodash/debounce.js');
and option 2 adds a bit of iteration code.
var _debounce = require('./lodash/debounce.js');
var Debounce = _interopRequireWildcard(_debounce);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key))
newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
Best Practice?
Is there a best practice for the average JS developer?
import * as debounce from 'lodash/debounce.js' // wildcard import
import debounce from 'lodash/debounce.js' // submodule import
import {debounce} from 'lodash' // deconstruction import
import _ , { debounce, throttle } from 'lodash'; // everything but the sink
import debounce from 'lodash.debounce' // method import
Method vs Submodule
As a side note there is also conflict about submodule vs. method imports i.e.
Submodule Import
import debounce from 'lodash/debounce'
Method Import
import debounce from 'lodash.debounce'
but I read that method imports don't share code and as such the memory footprint will be slightly larger, whereas submodules do, which would be important in larger libraries.
My gut feel is wildcard imports are a code smell (like infinity in science), but I feel I might misunderstand the nuances in the style guides.
Is the takeaway here that both are right, but only use wildcards
for maintainability / longevity when importing 3rd party code
smaller libraries or utilities a more efficient approach
namespacing?
or ...
...but I've seen this type of example more often than not.
import * as debounce from 'lodash/debounce.js'
The only reason I can imagine is maintainability i.e. debounce will remain debounce independent of export, and in this cases debounce will debounce as long as a debounce.js file exists.
While that's true, it's not particularly useful, because debounce in that example is just an object (specifically, it's called a module namespace object). It's just a container. It has properties for all the named exports in debounce.js and if there's also a default export, a property called default. Those (other than default of course) will still change if the exports in debounce.js change.
The difference between
OPTION 1:
import debounce from "lodash/debounce.js";
OR
OPTION 2:
import * as debounce from 'lodash/debounce.js'
The difference is in what they do, not (just) the output that (say) Webpack creates for them. They do different things.
import debounce from 'lodash/debounce.js' imports the default export from debounce.js and binds it to a local binding (loosely, variable) called debounce.
import * as debounce from 'lodash/debounce.js' imports ALL of the exports from debounce.js wrapped up in a module namespace object and binds that object to a local binding called debounce.
In the first example, assuming the default export of lodash/debounce.js is a function, you'd use it as:
const newFunction = debounce(someFunction, someDelay);
In the second example, you'd have to use a property on the module namespace object; assuming it's the default export, that would be:
const newFunction = debounce.default(someFunction, someDelay);
Those are fundamentally different.
So the best practice is to do the thing you actually want to do. Sometimes, you want the module namespace object, though (opinion!) I suspect that's fairly rare. Generally, you probably want to import specific exports, either the default one (import x from ...) or a named one (import { x } from ...).
Question
I have seen a large codebase where every file with constants looks something like this:
export const DEFAULT_ID = 0;
export const CURRENT_CODE = 'ABC123';
export default {
DEFAULT_ID,
CURRENT_CODE
};
They are using both a named as well as a default export for all constants. When it comes to how the constants are actually being imported, it seems that they are usually simply importing them as named exports:
import {CURRENT_CODE} from './whatever.consts';
Is there any use case for this practice? It’s unusual, since, normally, either a named or a default export is used, not both.
Research
I checked this question hoping to get some insight into why one would use them together in this manner, but I couldn’t find anything.
The closest thing I got to an answer was in this article. In the section “Why expose a symbol as both default and named exports?”, they provide a brief example of how this allows someone to use import {A, B} from './a' instead of needing to write something like import A, {B} from './a'. However, this explanation doesn’t make sense to me since the same syntax can be used if the constants are simply exported as named exports.
My Thoughts
The only reason I can think of is that this approach can give more flexibility when it comes to importing constants. I.e., it allows using both
import {DEFAULT_ID, CURRENT_CODE} from './whatever.consts';
let id = DEFAULT_ID, code = CURRENT_CODE;
and
import INITIALIZATION_CONSTS from './whatever.consts';
let id = INITIALIZATION_CONSTS.DEFAULT_ID, code = INITIALIZATION_CONSTS.CURRENT_CODE
for importing the constants.
Is this a valid reason for using this approach? Are there any best practices implications?
Is there any use case for this practice?
Not really. The only thing I can think of is backwards-compatibility, possibly related to how they are transpiling their code, if the module is a library used elsewhere.
The only reason I can think of is that this approach can give more flexibility when it comes to importing constants.
A default export is not necessary for that. You can easily use a namespace import with named exports only:
import * as INITIALIZATION_CONSTS from './whatever.consts';
let id = INITIALIZATION_CONSTS.DEFAULT_ID, code = INITIALIZATION_CONSTS.CURRENT_CODE
I am using D3.js V4 with the module and I would like to import few modules into a single3` namespace. The code snippet below is my current solution, is there a better way to do so?
const d3 = Object.assign(
{},
require('d3-axis'),
require('d3-selection'),
require('d3-format')
)
So whenever I need anything, I just call something like below
d3.format('.5s')
// OR
d3.select(something)
Is there a nicer way to import everything into a single d3 namespace?
import multiple 'export' with ES6 or TypeScript
A typesafe way
import * as d3Axis from 'd3-axis';
import * as d3Selection from 'd3-selection';
export const d3 = {...d3Axis, ...d3Selection};
That said, d3 was written before TypeScript and does not support type safety in its API decisions.
More
Yes, you need to design for type safety, e.g. if your library uses direct array access it is inherently unsafe https://basarat.gitbooks.io/typescript/docs/types/index-signatures.html
I wanted to double check to make sure I understand imports enough to know if it is ok to do import {_.identity} from 'underscore' opposed to import _ from 'underscore'? That is the only use of underscore if the particular file.
Thank you for your help
Looks like you're very close!
There are a few ways to do this.
IMO the cleanest way to do this goes like this:
import { map, reduce, somethingElse } from 'underscore'
Allowing you to call those methods as so:
map(things, thing => {
...
})
The '{ map, reduce } = ...' part is es6s destructuring assignment.
See the Mozilla docs page for more details on this!
Another way would be to do:
import map from 'underscore/map'
import reduce from 'underscore/reduce'
Personally, I'm not a big fan of this since it can start being a bit more cumbersome as more methods are pulled in but it does have one slight advantage, you can name the reference as you like:
import mappy from 'underscore/map'
import reducerify from 'underscore/reduce'
Though I wouldn't advise using those names!
Import: import * as _ from 'underscore'
https://underscorejs.org/#map
Example:
_.map(things, thing => {
...
})
I had a pull request feedback below, just wondering which way is the correct way to import lodash?
You'd better do import has from 'lodash/has'.. For the earlier version
of lodash (v3) which by itself is pretty heavy, we should only import
a specidic module/function rather than importing the whole lodash
library. Not sure about the newer version (v4).
import has from 'lodash/has';
vs
import { has } from 'lodash';
Thanks
import has from 'lodash/has'; is better because lodash holds all it's functions in a single file, so rather than import the whole 'lodash' library at 100k, it's better to just import lodash's has function which is maybe 2k.
If you are using webpack 4, the following code is tree shakable.
import { has } from 'lodash-es';
The points to note;
CommonJS modules are not tree shakable so you should definitely use lodash-es, which is the Lodash library exported as ES Modules, rather than lodash (CommonJS).
lodash-es's package.json contains "sideEffects": false, which notifies webpack 4 that all the files inside the package are side effect free (see https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free).
This information is crucial for tree shaking since module bundlers do not tree shake files which possibly contain side effects even if their exported members are not used in anywhere.
Edit
As of version 1.9.0, Parcel also supports "sideEffects": false, threrefore import { has } from 'lodash-es'; is also tree shakable with Parcel.
It also supports tree shaking CommonJS modules, though it is likely tree shaking of ES Modules is more efficient than CommonJS according to my experiment.
Import specific methods inside of curly brackets
import { map, tail, times, uniq } from 'lodash';
Pros:
Only one import line(for a decent amount of functions)
More readable usage: map() instead of _.map() later in the javascript code.
Cons:
Every time we want to use a new function or stop using another - it needs to be maintained and managed
Copied from:The Correct Way to Import Lodash Libraries - A Benchmark article written by Alexander Chertkov.
You can import them as
import {concat, filter, orderBy} from 'lodash';
or as
import concat from 'lodash/concat';
import orderBy from 'lodash/orderBy';
import filter from 'lodash/filter';
the second one is much optimized than the first because it only loads the needed modules
then use like this
pendingArray: concat(
orderBy(
filter(payload, obj => obj.flag),
['flag'],
['desc'],
),
filter(payload, obj => !obj.flag),
If you are using babel, you should check out babel-plugin-lodash, it will cherry-pick the parts of lodash you are using for you, less hassle and a smaller bundle.
It has a few limitations:
You must use ES2015 imports to load Lodash
Babel < 6 & Node.js < 4 aren’t supported
Chain sequences aren’t supported. See this blog post for alternatives.
Modularized method packages aren’t supported
I just put them in their own file and export it for node and webpack:
// lodash-cherries.js
module.exports = {
defaults: require('lodash/defaults'),
isNil: require('lodash/isNil'),
isObject: require('lodash/isObject'),
isArray: require('lodash/isArray'),
isFunction: require('lodash/isFunction'),
isInteger: require('lodash/isInteger'),
isBoolean: require('lodash/isBoolean'),
keys: require('lodash/keys'),
set: require('lodash/set'),
get: require('lodash/get'),
}
I think this answer can be used in any project easily and brings the best result with less effort.
For Typescript users, use as following :
// lodash.utils.ts
export { default as get } from 'lodash/get';
export { default as isEmpty } from 'lodash/isEmpty';
export { default as isNil } from 'lodash/isNil';
...
And can be used the same way as importing lodash :
//some-code.ts
import { get } from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return get(thing, 'someSubField', 'someDefaultValue')
}
Or if you prefer to keep the _ to avoid conflicts (ex. map from rxjs vs lodash)
//some-other-code.ts
import * as _ from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return _.get(thing, 'someSubField', 'someDefaultValue')
}
UPDATE :
Seems like the right way to export is :
export * as get from 'lodash/get';
export * as isEmpty from 'lodash/isEmpty';
export * as isNil from 'lodash/isNil';
...
But there is a weird collision with #types/lodash, I've removed this type package because I would get this error :
Module '"/../project/node_modules/#types/lodash/cloneDeep"' uses
'export =' and cannot be used with 'export *'.ts(2498)
UPDATE :
After some digging, I've turned tsconfig.json feature esModuleInterop to true, and it allows me to do the following :
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import isNil from 'lodash/isNil';
...
export { get, isEmpty, isNil, ... };
Note that this affects all your imports in your projects that has been defined as import * as lib from 'lib'. Follow the documentation to be sure it's suitable for you.
import { cloneDeep, groupBy } from 'lodash';
I think this is simpler when you don't need to convert array to lodash object by using _.
const groupData = groupBy(expandedData, (x) => x.room.name);
For those who want to keep using _ , then just import them like this:
import groupBy from 'lodash/groupBy';
import filter from 'lodash/filter';
import get from 'lodash/get';
window._ = {groupBy, filter, get};
I think the more cleaner way of importing lodash is just like this:-
import _ from 'lodash'
then you can use what ever you want just by using this underscore just like this:-
_.has()