My goal is to export an extended class called Entrance without using curly brackets (Let's say subclass).
The problem is I can't access to the subclass while I'm using default keyword on it and the browser gives me an error such as this:
Uncaught SyntaxError: Duplicate export of 'default'
Code:
// terminal.js
export default class Terminal {
constructor(output) {
this.output = output;
console.log(`This is Terminal`);
}
}
export default class Entrance extends Terminal {
constructor(output) {
super(output);
}
ticket() {
console.log(`Ticket please`);
}
}
// human.js
import Entrance from './terminal.js';
class People {
constructor(name, input) {
this.name = name;
this.input = new Entrance(this);
this.input.ticket();
}
}
const guy = new People('james');
Is this type of structure doesn't allow originally? Or did I miss something in the code?
Thanks for listening.
I adapted your project to Node.js because this is easier for testing. In the browser you can still use the .js extension but you need to reference the files as modules, not as scripts.
For Node.js use the extension .mjs for ECMAScript Modules ('import' and 'export' are a feature of ECMAScript modules).
You only need to export the identifiers you reference externally: (the 'Entrance' class).
Use 'node --experimental-modules ./human.mjs' to run it
// terminal.mjs: only 'Entrance' is exported
// no need to export 'Terminal' as long as it's name is not referenced outside
class Terminal {
constructor(output) {
this.output = output;
console.log(`This is Terminal`);
}
}
export default class Entrance extends Terminal {
constructor(output) {
super(output);
}
ticket() {
console.log(`Ticket please`);
}
}
// human.mjs
import Entrance from './terminal.mjs';
class People {
constructor(name, output) {
this.name = name;
this.input = new Entrance(this);
this.input.ticket();
}
}
const guy = new People('james');
If you want to reference also the Terminal class outside, dont use 'default' exports/imports (or create a top level object with Terminal and Entrance as members):
// terminal2.mjs
export class Terminal {
constructor(output) {
this.output = output;
console.log(`This is Terminal`);
}
}
export class Entrance extends Terminal {
constructor(output) {
super(output);
}
ticket() {
console.log(`Ticket please`);
}
}
// human2.mjs: import the whole module under the alias 'term'
import * as term from './terminal2.mjs';
class People {
constructor(name, output) {
this.name = name;
this.input = new term.Entrance(this);
this.input.ticket();
new term.Terminal(this);
}
}
// human3.mjs: using named imports which are directly usable
import { Terminal, Entrance} from './terminal2.mjs';
class People {
constructor(name, output) {
this.name = name;
this.input = new Entrance(this);
this.input.ticket();
new Terminal(this);
}
}
const guy = new People('james');
Now with default exports but encapsulated into a library object. This might be the standard way to do it, but only export the symbols you reference outside:
// terminal4.mjs: using a top level object and a default export
class Terminal {
constructor(output) {
this.output = output;
console.log(`This is Terminal`);
}
}
class Entrance extends Terminal {
constructor(output) {
super(output);
}
ticket() {
console.log(`Ticket please`);
}
}
const myLib = {Terminal, Entrance};
export default myLib;
// or just: export default {Terminal, Entrance};'
// human4.mjs
import term from './terminal4.mjs';
class People {
constructor(name, output) {
this.name = name;
this.input = new term.Entrance(this);
this.input.ticket();
new term.Terminal(this);
}
}
const guy = new People('james');
references:
'export': https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
'import': https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Node.js ECMAScript Modules: https://nodejs.org/api/esm.html
The error is shown because you are exporting both Terminal and Entrance classes as default.
if you need only Entrance class from terminal.js, remove export default from Terminal class.
Related
I have declared a class, like:
export default class Utils {
static deepClone(): object {
return {}
}
}
so when I want to use deepClone, I can:
// First One
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = new Utils()
}
create(){
return this.utils.deepClone()
}
}
or:
// Second One
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = Utils
// this is an optional
// in my child class I need't to import Utils
// I can use this.utils.deepClone() directly
}
create(){
return Utils.deepClone()
}
}
I wonder which is a better way to imply Element class
Looking forward to your reply, I can’t thank you enough
The second way is more correct but it has a problem that you should create an instance of Utils class to use each property which isn't a static method.
The output of a class that you didn't create an instance is a function instead of an object of prototypes.
./Utils.js
export default class Utils {
static deepClone(): object {
return {}
}
public deepExtend() {
// deepClone code
}
}
Use in another file:
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = new Utils();
}
create(){
return Utils.deepClone()
}
extend(){
return this.utils.deepExtend()
}
}
I would return export new of the Utils and I will pass it in the constructor of your Element as an Instance, something like:
IUtils = someInterface;
export default class Element {
constructor(utils: IUtils){
this.utils = utils;
}
create(){
return this.utils.deepClone()
}
}
In these way it:
Doesn't create new instances for nothing
Uses other instances that you can pass to your Element class
Is testable
in index.js I have.
import PageLoader from './pageLoader';
$(function() {
const pageLoader = new PageLoader();
});
and pageloader.js
class PageLoader{
constructor(){
{
this.customer = null;
this.data = [];
this.init();
}
}
init() { }
}
module.exports = PageLoader;
everything works fine. but if I import a class from page loader.
import Customer from './customer';
class PageLoader{
constructor(){
{
this.customer = null;
this.data = [];
this.init();
}
}
init() { }
}
module.exports = PageLoader;
and customer.js
class Customer{
constructor(){
this.data = [];
this.init();
}
init() {
}
}
module.exports = Customer;
I receive
WARNING in ./src/index.js 10:23-33 "export 'default' (imported as
'PageLoader') was not found in './pageLoader'
module.exports
syntax is from Modules (which are largely used in NodeJs - the counterpart of it is require rather than import). If you want to use import, you need to use export clause, which is from es6 modules
export default PageLoader
you could also do named exports
export { PageLoader };
and then
import { PageLoader } from './pageLoader';
Further reading
I'm migrating some old js to ts. The file is of form (function implementations omitted for clarity):
// component.js
const Component = {}; // 'namespace' for components
Component.Base = function() {}
Component.A = function() {} // extends Base
Component.A.prototype.doStuffA = function() {}
Component.B = function() {} // extends Base
Component.B.prototype.doStuffB = function() {}
Component.C = function() {} // extends Base
// ... 100 other components, 2000 lines of code
export default Component;
In js, to use the file, I can do:
import Component from './component';
// 1. Instantiate one component
const compA = new Component.A();
// 2. or create multiple components
const threeComps = ['A', 'B', 'C'].map(name => new Component[name]() );
But in ts, I cannot even instantiate one component:
import Component from './component';
const compA: Component.A = new Component.A();
// tsc Cannot find namespace 'Component'
Question: What is the (quick) way to convert component.js into valid typescript, preferably keeping as many type-checks available as possible such
that
const compA: Component.A = new Component.B()
will be flagged as an error by the compiler.
I tried appending the following to the end of file:
namespace Component {
interface A {};
interface B {};
interface C {};
}
This seems to compile into correct javascript, but I would have to add all properties into interfaces. Seems tedious and violation of DRY-principle.
If you are going to migrate to TypeScript, you could immediately take advantage of the class syntax in your component.ts file:
export class Base {
}
export class A {
doStuffA() {}
}
export class B {
doStuffB() {}
}
export class C extends Base {
}
You can consume it using an import alias:
import * as Component from './component';
const a = new Component.A();
Or you can be selective with what you import:
import { A } from './component';
const a = new A();
Export Default / Modules Mixed With Namespaces
On the whole, the experts are saying that export default is a bad thing and that you shouldn't mix modules and namespaces.
You can do it, if you feel you must. here is the example with a namespace / default:
namespace Component {
export class Base {
}
export class A {
doStuffA() {}
}
export class B {
doStuffB() {}
}
export class C extends Base {
}
}
export default Component;
And the use:
import Component from './component';
const a = new Component.A();
So I'm trying to extend a class in node js and the compiler keeps returning the following error:
TypeError: Class extends value #<Object> is not a function or null
I checked that I was exporting the class correctly and I am, any ideas? I'll post my code below:
/handler/venue.js:
var VenueViews = require('../views/venue'); // If I remove this the error will dissapear (as expected)
class Venue {
constructor(data) {
this.setDataHere = data;
}
main () {
var View = new VenueViews(); // This doesn't run
}
}
module.exports = Venue;
/views/venue.js:
var Venue = require('../handlers/venue');
console.log (Venue) // This returns {} ???
class VenueViews extends Venue {
constructor() {
super();
}
}
module.exports = VenueViews;
I know that node supports these es6 features, so I'm unsure why they aren't working?
Edit:
I'm not sure if this is suppose to happen but, when I log my Venue require it returns an empty object {}.
console.log (Venue) // This returns {} ???
So it turns out I had a circular reference in my code, where I was importing the class that was extending, into the class that itself was extending (tongue twister :P).
The obvious fix was to simply remove the extends reference and find another way of doing what I was trying to achieve. In my case it was passing the Venue class properties down into the VenueViews constructor.
E.g var x = VenueViews(this)
In my instance, it was the same issue as #James111 was experiencing (circular import) due to a factory pattern I was trying to set up in Typescript. My fix was to do move the code into files, similar to the following:
// ./src/interface.ts
import { ConcreteClass } from './concrete';
export interface BaseInterface {
someFunction(): any;
}
export class Factory {
static build(): BaseInterface {
return new ConcreteClass();
}
}
// ./src/base.ts
import { BaseInterface } from './interface';
class BaseClass implements BaseInterface {
someFunction(): any {
return true;
}
}
// ./src/concrete.ts
import { BaseClass } from './base';
export class ConcreteClass extends BaseClass {
someFunction(): any {
return false;
}
}
I had faced similar issue, after checking all the workaround finally issue got resolved by deleting the node_modules folder and run npm i.
In ES6, given the following example:
export default class MyStyle extends Stylesheet {
static Color = {
mainDark: '#000'
}
static Comp = {
...
color: Color.mainDark
}
}
How can I access Color.mainDark (the static field)?
You can access it as you would expect, however if I recall there were some issues when using Babel and exporting the class immediately, so export after defining the class if you're having problems:
class MyStyle extends Stylesheet {
static Color = {
mainDark: '#000'
}
someMethod() {
console.log(MyStyle.Color.mainDark);
}
}
export default MyStyle;
You can read more about the Babel issue in an answer Marian made on a similar question, which is supposedly fixed in Babel 6.2.1.
'use strict';
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static createGuest() {
return new User("guest", "site");
}
};
let user = User.createGuest();
alert( user.firstName ); // guest
alert( User.createGuest ); // createGuest ... (function)
And const:
'use strict';
class Menu {
static get elemClass() {
return "menu"
}
}
alert( Menu.elemClass ); // menu
use call to context object --- this
ES6 - Call static method within a class