Hi I switched over to ECMAScript-6 javascript syntax a little while ago and am loving it! One thing I noticed and couldn't find a definitive answer on is using nested destructing syntax on an import. What I mean is something like this..
Lets say I have a file that looks like this.
export const SomeUtils = _.bindAll({ //lodash _
someFunc1(params){
// .... stuff here
},
someFunc2(params){
// .... stuff here
},
someFunc3(params){
// .... stuff here
}
});
// ... many more of these
I have been doing something like this to get a specific function
import {Utils} from '../some/path/to/utils';
var {someFunc2} = Utils;
To get to the point.. Is there a way to do a single line import for someFunc2? like how you can do the nested object destruction assignment with brackets? (Aka: {Utils: [{someFunc2}]}) ?
I used to do var someFunc2 = require('../some/path/to/utils').someFunc2; but I can't seem to figure out how to do it with an import statement
No, ES6 module imports do not provide a destructuring option. The only feature they have are named exports (but without nesting). They are meant to exactly replace your require(…).someFunc2 pattern.
In your specific case, I don't see any reason why you would be exporting a single object as a named export. Just use
export function someFunc1(params){
// .... stuff here
}
export function someFunc2(params){
// .... stuff here
}
export function someFunc3(params){
// .... stuff here
}
so that you then can do
import {someFunc2} from '../some/path/to/utils';
To achieve what you are looking for you need to export as default:
const Utils = _.bindAll({ //lodash _
someFunc1(params){
// .... stuff here
},
someFunc2(params){
// .... stuff here
},
someFunc3(params){
// .... stuff here
}
});
export default Utils;
You can then either import the whole thing of just what you need...
import Utils, { someFunc2 } from '../some/path/to/utils';
Related
I'm sure this is obvious, but I'm not seeing why this isn't working at the moment...
I have a file in a Vue project that exports keycodes in a couple different formats, one to be used for constants (allCodes) and another to be used for Vue (keyCodes):
export default {
allCodes,
keyCodes
};
When I try to import one of them using deconstruction like this, I get an error:
import { allCodes } from '#/helpers/keycodes';
21:17-25 "export 'allCodes' was not found in '#/helpers/keycodes'
warning in ./src/mixins/GlobalKeyPressHandler.js
However, importing then referring to the key by name works:
import object from '#/helpers/keycodes';
console.log('allCodes', object.allCodes);
What gives?
If you want named exports it should be
export {
allCodes,
keyCodes
};
Currently you are exporting an object as default.
BTW "I try to import one of them using deconstruction like this" if you mean that you are trying to use destructuring assignment in imports it wont work. Named import statement is not object destructuring though it looks alike in simplest case.
If you want to have default export than you should make assignment below the import statement
export default {
allCodes,
keyCodes
};
// importing side
import keycodes from '#/helpers/keycodes';
const { allCodes } = keycodes;
Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters
I am having a hard time understanding exporting and importing stuff in typescript, how should this one be constructed for example?
src/functions/handle.ts:
export default function handle() {
// do something here.
console.log("It is handled");
}
src/functions/index.ts:
import handle from './handle';
export default {
handle
};
src/run.ts:
import allFunctions from './functions';
console.log(allFunctions);
If I run node dist/run.js (I am compiling into distdirectory) after compiling, I get undefined. But if I use
import allFunctions from './functions/'
(notice the "/" at the end)
it references to an object that includes the exported function.
I also tried using export * from './handle' but the result is the same.
What is the correct way to accomplish this?
I guess what you are trying to achieve is to re-export all your functions from a single file so you just have to import that file to access them all
First of all, you cannot re-export default exports, so you will have to go for named exports. Anyway, avoid default exports, they do not provide much benefits and confuse everyone.
src/functions/handle.ts:
export function handle() {
// do something here.
console.log("It is handled");
}
src/functions/index.ts::
export * from './handle';
export * from './anotherFile';
...
src/run.ts:
import * as allFunctions from './functions/index'
Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters
Seemingly a very simple task...
export default function() {
return {
googleClientID:'xxxx'
}
}
Is it the best way to export object literal with app settings?
You can export object itself:
export default {
googleClientID:'xxxx'
};
The difference is that in your case you will get brand new object every time you call exported function. In this case you will get the same object every time. Depends on what you need.
You can simply export an object
export default { googleClientID:'xxxx' };
A default export can be a function, a class, an object or anything else. This value is to be considered as the "main" exported value since it will be the simplest to import.
#madox2 's and #void 's answer may be some kind of common misunderstanding.
I just ran into a similar problem while issuing a PR to DefinitelyTyped -- #18725. The typescript compiler complains about the generated files.
A example should be:
// ...
import zip from "./zip";
import zipObject from "./zipObject";
import zipObjectDeep from "./zipObjectDeep";
import zipWith from "./zipWith";
export default {
// ...
zip,
zipObject,
zipObjectDeep,
zipWith
};
At the first glance, i didn't think its my problem. Because i just copy the code from lodash-es. But then i can't find any simple approach to remove the errors.
So I go to the spec for answer. Wow, the spec doesn't talk about default export of an object, if I read it right.
Conclusion:
The follow is spec-respected:
export { googleClientID:'xxxx' }
Just found some more references:
https://medium.com/#timoxley/named-exports-as-the-default-export-api-670b1b554f65#a279
https://medium.com/#rauschma/note-that-default-exporting-objects-is-usually-an-anti-pattern-if-you-want-to-export-the-cf674423ac38
Exporting: not nicest, but handy when importing later.
export const ROOT = '/tmp/test'
export const EXT = '.backup'
// ... and so on
Importing: cleanest usage (and we usually import more than export).
import { ROOT, EXT } from './literals.js'
const file = ROOT + yourFileName + EXT