I have a Namespacing.js with something like the following
(function(){
window.GlobalObject = {
foo : function() { console.log("bar"); }
}
})();
Then i have another MyScript.js
GlobalObject.newAttribute = { ... }
So i'm now bundling with webpack and i was tryng to use modules on this, but i couldnt manage to do so.
At Namespacing.js i added at the end:
export default GlobalObject;
Then i tryed to import it in MyScript.js
import GlobalObject from "Namespacing"
But then my webpack gets me an error
[14:58:44] GulpUglifyError: unable to minify JavaScript
Caused by: SyntaxError: Unexpected token: name (Kneat) (line: 1, col: 7, pos: 7)
Does any1 knows a good way of doing this export/import ?
To switch to import/export, you can't just add exports to your existing files, you need to change their structure (ever so slightly).
For instance, Namespacing.js would have either:
export const GlobalObject = {
foo : function() { console.log("bar"); }
};
...to export a named symbol GlobalObject. That would be imported like this:
import { GlobalObject } from './Namespacing.js';
(You could use an as clause if you wanted a different name locally in the importing module.)
Or you could export that object as the default export:
export default {
foo : function() { console.log("bar"); }
};
...and import it like this:
import GlobalObject from './Namespacing.js';
Note that in that case, you can use any name you want for GlobalObject in the module in which you're importing it (no need for as as clause).
Don't worry about the fact that it involves removing the IIFE; modules are inherently scoped, module code doesn't run at global scope.
Related
When one imports a specific value from another file, does the entire file that has been exported from run in the file importing? For example if I wanted to import the function "hello" from file b, into file a, would file b run in file a?
An example being:
File A:
import {func} from 'fileB.js';
File B:
let func = function(){...}
console.log(`Hello`);
export {func};
Would Hello appear in the console of file A, and if it would, under what circumstances. For example, would it be when the import statement is run, or when the func is called. If it would not run, are there any ways to make it so it does. For example if I exported the entire file (if that's possible) would the Hello appear under certain circumstances?
The imported file will be run. An easy way to both understand and remember this is dynamic exports:
export let Foo;
if (window.Foo === undefined) {
Foo = class Foo { ... }
} else {
Foo = window.Foo;
}
In order to know what was exported to begin with, the code needs to be run. Otherwise, it would be equal to solving the halting problem.
if you are using webpack import or require
declare like this
const Logger = function() {
}
export { Logger };
use it
import { Logger } from '../class/Logger';
let logger = new Logger();
#My use case
I have a utils mixin module that injects lodash as a method in all .vue (single file components) components:
// utilsMixin.js
import _ from 'lodash';
import moment from 'moment';
export default {
computed: {
_: () => _,
moment: () => moment,
},
};
// main.js
// ...
import utilsMixin from './utilsMixin';
Vue.mixin(utilsMixin);
Therefore, all my project can use _ and moment as methods.
#The issue
If I use _ in the <template> part, no linter issues, as _ is hidden inside this variable from vue:
// MyFancyComponent.vue
<template>
<myComponent
v-for="item in _.get(this, 'myObject.collection', [])"
>
<div> {{ item }} </div>
</myComponent>
But when I use it in the <script> part:
// MyFancyComponent.vue
// ...
<script>
export default {
name: 'MyFancyComponent',
methods: {
find: ()=> _.find // [eslint] '_' is not defined. [no-undef]
};
</script>
Eslint complains from [eslint] '_' is not defined. [no-undef] issues, but I still can use it without importing _, and I can stop linter from complaining by declaring global lodash:
/* global _ */
Note that I still have other .js files that have not lodash as global, and I need linter to complain if I use lodash there:
#Expected behavior
I expect '_' to be detected as a global variable only in my vue Single File Components, but to be undefined in my JavaScript files (and therefore I would need to import lodash in .js files).
#Intuitive solution
I would expect to be able to configure globals inside .eslintrc.js that would work only inside Single File Components. As an approach, it could look like:
// .eslintrc.js
// ...
globals: {
vue: {
'_': false,
moment: false
},
js: {
process: false,
isFinite: true
}
},
// ...
Your problem is that you are missing how Vue.js is injecting this into your component. Here is what it will look like if you are properly using the Mixin within Vue.js (within the script):
// MyFancyComponent.vue
// ...
<script>
export default {
name: 'MyFancyComponent',
methods: {
find(...args) {
return this._.find.bind(this._, args);
}// [eslint] '_' is not defined. [no-undef]
};
</script>
You use this._ to reference Lodash because you have bound it to the instance of the component, not made it a globally-referenced object. The reason that it works, but ESLint is complaining is because you have imported the library as a global object within your mixin, but you didn't import it within this specific file, so ESLint is complaining because it doesn't know that you did a global import within another file and bound it to every single component to make it available to those individual components.
How could I declare a third party module which looks like this:
in third party module:
module.exports = function foo(){
// do somthing
}
in my code:
import * as foo from 'foo-module'; // Can not find a declaration module for ...
foo();
Check out the documentation on working with 3rd party modules.
How to write the declaration depends a lot on how the module was written and what it exports.
The example you've given is a CommonJS module (module.exports = ...) which is not really a valid ES6 module, because ES6 cannot export a function as the module (it can only export function members or a default function).
Update for TypeScript 2.7+
With the added esModuleInterop compiler option you no longer need to use the "namespace hack" shown below for CommonJS modules that have a non-ES6 compatible export.
First, make sure you've enabled esModuleInterop in your tsconfig.json (which is now included by default with tsc --init):
{
"compilerOptions" {
...
"esModuleInterop": true,
...
}
}
Declare your foo-example in a .d.ts file like this:
declare module "foo-module" {
function foo(): void;
export = foo;
}
Now you can import it as a namespace like you wanted:
import * as foo from "foo-module";
foo();
Or as a default import:
import foo from "foo-module";
foo();
Older workaround
You can declare your foo-example in a .d.ts file like this:
declare module "foo-module" {
function foo(): void;
namespace foo { } // This is a hack to allow ES6 wildcard imports
export = foo;
}
And import like you wanted:
import * as foo from "foo-module";
foo();
Or like this:
import foo = require("foo-module");
foo();
The documentation has a good resource on declaration files and some templates for various kinds of declaration files.
I had a similar problem. And struggled to add a type definition to my project. Finally, I was able to achieve it using the following steps.
This is some module (just with constants), lets call it some-module - node_modules/some-module/index.js.
'use strict';
exports.__esModule = true;
var APPS = exports.APPS = {
ona: 'ona',
tacq: 'tacq',
inetAcq: 'inetAcq'
};
First I add to tsconfig.json baseUrl and typeRoots
{
...
"compilerOptions": {
...
"baseUrl": "types",
"typeRoots": ["types"]
}
...
}
Second in my project root I create folder types with same folders structure for the module types/some-module/index.js and place the code:
declare module 'some-module' {
type Apps = {
ona: string;
tacq: string;
inetAcq: string;
};
let APPS: Apps
}
Finally I can import it in my my-file.ts with typings!
import { APPS } from 'some-module';
I have an external library thing.d.ts file with a global definition inside:
declare var thing: ThingStatic;
export default thing;
I reference npm module in my TypeScript:
import thing from 'thing';
...
thing.functionOnThing();
When I transpile the TS (targeting ES6) it looks something like this:
const thing_1 = require("thing");
...
thing_1.default.functionOnThing();
This then throws an error:
Cannot read property 'functionOnThing' of undefined
Why is TypeScript adding .default between thing_1 and functionOnThing()?
There is no property named default on ThingStatic, and no default property on the underlying JS object that the .d.ts file defines.
Why is TypeScript adding the property and how do I stop it?
import thing from 'thing';
This line of code means "import the default export from the module 'thing' and bind it to the local name thing".
TypeScript does as you requested and accesses the default property of the module object.
What you probably meant to write was
import * as thing from 'thing';
This appears to be a bug with global TS definitions and "module": "commonjs" in the tsconfig.json.
You can either use global TS definitions and stitch all your output into a single file, or you can use modules and directly import them.
The error here is due to the require returning the module context, and the name of the default being irrelevant - it always becomes default...
declare var thing: ThingStatic;
export thing; // Explicit export for thing
export default thing; // Default export for thing
Now require will return this context, so with commonjs modules:
import module from 'thing';
var thing = module.default; // From the default export
var alsoThing = module.thing; // From the named export
However, I've found this to be inconsistent, so switched to es6 modules:
import thing from './thing'; // Import default
import { thing } from './thing'; // Import named
const thing = (await import('path/to/thing.js')).default; // Import dynamic
I am using TypeScript 1.6 with ES6 modules syntax.
My files are:
test.ts:
module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
main.ts:
import App from './test';
var a = new App.SomeClass();
When I am trying to compile the main.ts file I get this error:
Error TS2306: File 'test.ts' is not a module.
How can I accomplish that?
Extended - to provide more details based on some comments
The error
Error TS2306: File 'test.ts' is not a module.
Comes from the fact described here http://exploringjs.com/es6/ch_modules.html
17. Modules
This chapter explains how the built-in modules work in ECMAScript 6.
17.1 Overview
In ECMAScript 6, modules are stored in files. There is exactly one
module per file and one file per module. You have two ways of
exporting things from a module. These two ways can be mixed, but it is
usually better to use them separately.
17.1.1 Multiple named exports
There can be multiple named exports:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
...
17.1.2 Single default export
There can be a single default export. For example, a function:
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
Based on the above we need the export, as a part of the test.js file. Let's adjust the content of it like this:
// test.js - exporting es6
export module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
export class OtherClass {
getName(): string {
return 'name';
}
}
}
And now we can import it in these three ways:
import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";
And we can consume imported stuff like this:
var a1: app1.App.SomeClass = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();
var b1: app2.App.SomeClass = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();
var c1: App.SomeClass = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();
and call the method to see it in action:
console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())
Original part is trying to help to reduce the amount of complexity in usage of the namespace
Original part:
I would really strongly suggest to check this Q & A:
How do I use namespaces with TypeScript external modules?
Let me cite the first sentence:
Do not use "namespaces" in external modules.
Don't do this.
Seriously. Stop.
...
In this case, we just do not need module inside of test.ts. This could be the content of it adjusted test.ts:
export class SomeClass
{
getName(): string
{
return 'name';
}
}
Read more here
Export =
In the previous example, when we consumed each validator, each module only exported one value. In cases like this, it's cumbersome to work with these symbols through their qualified name when a single identifier would do just as well.
The export = syntax specifies a single object that is exported from the module. This can be a class, interface, module, function, or enum. When imported, the exported symbol is consumed directly and is not qualified by any name.
we can later consume it like this:
import App = require('./test');
var sc: App.SomeClass = new App.SomeClass();
sc.getName();
Read more here:
Optional Module Loading and Other Advanced Loading Scenarios
In some cases, you may want to only load a module under some conditions. In TypeScript, we can use the pattern shown below to implement this and other advanced loading scenarios to directly invoke the module loaders without losing type safety.
The compiler detects whether each module is used in the emitted JavaScript. For modules that are only used as part of the type system, no require calls are emitted. This culling of unused references is a good performance optimization, and also allows for optional loading of those modules.
The core idea of the pattern is that the import id = require('...') statement gives us access to the types exposed by the external module. The module loader is invoked (through require) dynamically, as shown in the if blocks below. This leverages the reference-culling optimization so that the module is only loaded when needed. For this pattern to work, it's important that the symbol defined via import is only used in type positions (i.e. never in a position that would be emitted into the JavaScript).
Above answers are correct. But just in case...
Got same error in VS Code. Had to re-save/recompile file that was throwing error.
How can I accomplish that?
Your example declares a TypeScript < 1.5 internal module, which is now called a namespace. The old module App {} syntax is now equivalent to namespace App {}. As a result, the following works:
// test.ts
export namespace App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
// main.ts
import { App } from './test';
var a = new App.SomeClass();
That being said...
Try to avoid exporting namespaces and instead export modules (which were previously called external modules). If needs be you can use a namespace on import with the namespace import pattern like this:
// test.ts
export class SomeClass {
getName(): string {
return 'name';
}
}
// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();
In addition to A. Tim's answer there are times when even that doesn't work, so you need to:
Rewrite the import string, using the intellisense. Sometimes this fixes the issue
Restart VS Code
I had this issue and I had forgotten to export the Class.
In addition to Tim's answer, this issue occurred for me when I was splitting up a refactoring a file, splitting it up into their own files.
VSCode, for some reason, indented parts of my [class] code, which caused this issue. This was hard to notice at first, but after I realised the code was indented, I formatted the code and the issue disappeared.
for example, everything after the first line of the Class definition was auto-indented during the paste.
export class MyClass extends Something<string> {
public blah: string = null;
constructor() { ... }
}
Just in case this may works for you as it did form me, i had this files
//server.ts
class Server{
...
}
exports.Server = Server
//app.ts
import {Server} from './server.ts'
And this actually raised an error but i changed server.ts to
//server.ts
export class Server{
...
}
and it worked 😎👌
Note: i am using this config
"target": "esnext",
"module": "commonjs",
I faced the same issue in a module that has no exports. I used it for side-effects only. This is what the TypeScript docs say about importing side-effects modules:
Though not recommended practice, some modules set up some global state that can be used by other modules. These modules may not have any exports, or the consumer is not interested in any of their exports. To import these modules, use:
import "./my-module.js";
In that situation, you can fix the "File is not a module" error by simply exporting an empty object:
// side-effects stuff
export default {};
I faced the same issue ("File is not a module error") for import js in vue component
import handleClientLoad from "../../../public/js/calendar.js"
I do this and solve it
// #ts-ignore
import handleClientLoad from "../../../public/js/calendar.js"
The file needs to add Component from core hence add the following import to the top
import { Component } from '#angular/core';