Issue importing legacy JavaScript class_1.class as a constructor in TypeScript with Webpack

I am currently in the process of transitioning a project from JavaScript to TypeScript. The original JavaScript code is legacy and was not structured for exporting/importing, but rather concatenated together. I am facing challenges when trying to import these legacy JavaScript classes into my TypeScript code and encounter errors when attempting to instantiate an imported JavaScript class.

My build setup involves using Webpack to compile the final app.js file with Babel being run within Webpack.

Directory structure:

webpack.config.js
tsconfig.json
.babelrc
legacy-js/
--LegacyJSClass.js
new-ts/
--NewTSClass.ts
ts-build/
--legacy-js/
----LegacyJSClass.js
----LegacyJSClass.js.map
--new-ts/
----NewTSClass.js
----NewTSClass.js.map
dist/
--webpack-output-file.js

./legacy-js/LegacyJSClass.js:

class LegacyJSClass {
    constructor(data) {
        data = data || {};
        this.ExampleJsProp = data.ExampleProp;
    }
}

export { LegacyJSClass };

./new-ts/NewTSClass.ts

import { LegacyJSClass } from "../legacy-js/LegacyJSClass";

export class NewTSClass {
    ExampleTsProp: any = new LegacyJSClass();
    constructor() {
        console.log(this.ExampleTsProp);
    }
}

let tsClass = new NewTSClass();

./tsconfig.json

{
  "compilerOptions": {
    "outDir": "./ts-build",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "traceResolution": true
  },
  "compileOnSave": true,
  "include": [
    "./new-ts/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

./webpack.config.js

var webpack = require('webpack');
var ManifestPlugin = require('webpack-manifest-plugin');

module.exports = {
    name: 'app',
    devtool: 'inline-source-map',
    plugins: [
        new ManifestPlugin()
    ],
    module: {
        preLoaders: [
            {
                test: /\.js%/,
                loader: 'eslint',
                exclude: /node_modules/
            },
            {
                test: /\.js$/,
                loader: 'source-map-loader',
                exclude: /node_modules/
            }
        ],
        loaders: [
            {
                test: /\.ts?$/,
                loader: 'awesome-typescript-loader',
                exclude: /node_modules/
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: ['env']
                }
            }
        ]
    },
    resolve: {
        extensions: [".ts", ".js"]
    },
    entry: __dirname + '/new-ts/NewTSClass',
    output: {
        filename: 'webpack-output-file.js',
        path: __dirname + '/dist'
    },
    eslint: {
        failOnWarning: false,
        failOnError: true
    },
    externals: {
        "jquery": "jquery"
    }
};

./.babelrc

{
  "presets": [ "env" ],
  "plugins": [
    "transform-remove-export" // This removes the export statements from the LegacyJS since it is still contatenated together and output as one big file and doesn't use a module loader.
  ]
}

Although everything compiles without errors, attempting to load webpack-output-file.js in the browser results in the following error:

NewTSClass.ts:20 Uncaught TypeError: LegacyJSClass_1.LegacyJSClass is not a constructor
    at new NewTSClass (NewTSClass.ts:20)
    at Object.<anonymous> (NewTSClass.ts:26)
    at __webpack_require__ (bootstrap 4a128ff…:19)
    at Object.defineProperty.value (bootstrap 4a128ff…:39)
    at bootstrap 4a128ff…:39

Further investigation reveals that Typescript is transpiling correctly:

./ts-build/legacy-js/LegacyJSClass.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var LegacyJSClass = (function () {
    function LegacyJSClass(data) {
        data = data || {};
        this.ExampleJsProp = data.ExampleProp;
    }
    return LegacyJSClass;
}());
exports.LegacyJSClass = LegacyJSClass;
//# sourceMappingURL=LegacyJSClass.js.map

./ts-build/new-ts/NewTSClass.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var LegacyJSClass_1 = require("../legacy-js/LegacyJSClass");
var NewTSClass = (function () {
    function NewTSClass() {
        this.ExampleTsProp = new LegacyJSClass_1.LegacyJSClass();
        console.log(this.ExampleTsProp);
    }
    return NewTSClass;
}());
exports.NewTSClass = NewTSClass;
var tsClass = new NewTSClass();
//# sourceMappingURL=NewTSClass.js.map

However, after going through Webpack/Babel, the transformation looks different:

./dist/webpack-output-file.js

/******/

// Webpack bootstrap stuff...

/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var LegacyJSClass_1 = __webpack_require__(1);
    var NewTSClass = (function () {
        function NewTSClass() {
            this.ExampleTsProp = new LegacyJSClass_1.LegacyJSClass();
            console.log(this.ExampleTsProp);
        }
        return NewTSClass;
    }());
    exports.NewTSClass = NewTSClass;
    var tsClass = new NewTSClass();

/***/ }),
/* 1 */
/***/ (function(module, exports) {
    "use strict";

    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

    var LegacyJSClass = function LegacyJSClass(data) {
        _classCallCheck(this, LegacyJSClass);

        data = data || {};
        this.ExampleJsProp = data.ExampleProp;

    };

/***/ })
/******/ ]);
// Sourcemap info...

I have tried various solutions without success. Changing the JS module export to export default LegacyJSClass throws the error

LegacyJSClass_1.default is not a constructor
.

The reason behind wanting this to work is to avoid creating separate type files for each legacy class used, given the extensive nature of the legacy JS code. As other parts of the application rely on legacy JS, it's crucial for both TypeScript and JavaScript components to seamlessly coexist during the transition process.

Answer №1

It appears that you are utilizing the

babel-plugin-transform-remove-export
, which eliminates all exports from your JavaScript files. The comment in your .babelrc may not be accurate if you are using webpack, as webpack relies on actual exports. The import you are attempting to use may not exist in the module processed by babel, as evidenced by the lack of export in module 1 of your webpack output. If you attempt to log out LegacyJSClass in your TypeScript file, you will notice it is undefined, indicating it is not a constructor.

To resolve this issue, consider removing the plugin from your .babelrc and avoid reconfiguring presets in your webpack config for babel-loader, as it will utilize the existing .babelrc.

{
  "presets": [ "env" ]
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Angular to make a request to a NodeJS+Express server for a simple GET operation

I need help with making a successful GET request from my Angular component to a NodeJS+Express server. someComponent.ts console.log("Before"); // send to server console.log(this.http.get('/email').map((res:Response) => { console.log(" ...

When executing JavaScript code, the file remains unchanged and does not alter the URL

I want my form to check a SQL database upon submission and execute a JavaScript file that captures the data into a constant. However, instead of running the JS script on the page as desired, it redirects to a new URL. Is there a way to ensure that the JS ...

Is it possible to use the setState function in a React functional component when the form is loading

Implementing a React component using hooks: Requirement: Upon page load, data should populate from the backend into dropdowns and tables with checkboxes. When the Submit button is clicked, the consolidated object of default selected values should be used ...

Exploring the power of Jade and Angular through implementing a for loop within a table structure

I'm brand new to using Jade and Angular, and I could really use a hint from someone more experienced. ... - for (var i = 0; i < p.length; i++) tr td= i + 1 td= price(value='p[i].somedbstuff') ... I want the la ...

Webpack Error: SyntaxError - an unexpected token found =>

After transferring my project to a new machine, I encountered an error when running webpack --watch: C:\Users\joe_coolish\AppData\Roaming\npm\node_modules\webpack\bin\webpack.js:186 outputOption ...

Enhanced Slider Display featuring Multiple Posts for Improved Performance

My Sample Page features a slider that displays over 200 posts, each containing 5 images. However, the slider loads all the images at once, causing my page speed to be very slow. I am looking for an optimized way to display the slider without compromising l ...

Employ the express platform to refrain from responding to particular inquiries

Is there a way for my server to not respond at all when receiving a specific user-agent in the request header, while still serving HTML normally for other browsers? I tried different methods like using res.status(404).end() and res.destroy(), but they did ...

What is the fastest way to efficiently refresh a substantial BufferGeometry?

When using a BufferGeometry to render thousands of cubes forming terrain, I encountered difficulty in updating the geometry when changing the position of a single cube. For instance, this is the code used to initialize the geometry: (My tests are based on ...

Exploring elementary Expressjs query concepts

Just getting started with nodejs and feeling a bit confused. I have a form on my website where users can submit information, and I want to display a response message in an HTML element once the form is submitted. I've been using body parser and Jade v ...

The Typescript compiler has trouble locating the definition file for an npm package

Recently, I released an npm package that was written in typescript. However, I have been facing difficulties in getting the definition recognized by typescript (webback and vscode). The only workaround that has worked for me so far is creating a folder wit ...

What could be causing Vite to not locate the '.vue' loader during the Vue 3 migration build process?

Currently in the process of upgrading a Vue 2 project to Vue 3 by utilizing the migration build and vite (https://v3-migration.vuejs.org/breaking-changes/migration-build.html#overview) I've completed steps 1-4 (skipping 4 as I'm not using typesc ...

Utilizing Unidirectional Binding within an AngularJS Directive

I have a directive set up here: myApp.directive('stoplight', function() { return { restrict:'E', transclude: true, scope: { value: '@' }, link: function(scope, element) ...

What is the optimal level of safety logic to incorporate into my proprietary components?

Having developed numerous React components, setting propTypes, writing tests, and occasionally defining default props, I find myself pondering the balance between safety and efficiency. Experimenting with Flow types has led me to consider implementing addi ...

Converting JSON Arrays into Typescript Arrays

I am working with a JSON file that contains an array object like this: [ { "VergiNo": "XXXXXXX" }, { "VergiNo": "YYYYYY" }, { "VergiNo": "ZZZZZZ" } ] After importing this JSON file into my Typescript file, import * as companies f ...

Mongodb/mongoose encountering issues with saving data only once, resulting in a 500 error

When I send json to my node.js/express app, it successfully receives the data and performs the desired action, but only once. After starting the appjs for the first time, it returns a 200 response code and saves the data to my mongodb. However, any subsequ ...

Having trouble retrieving the default selected value using the index in Angular Material's mat-button-toggle functionality

I'm encountering an issue with setting the default value for a toggle button group. The code is simple and the toggle buttons are correctly fetching values from the index, but I can't seem to get one of them to be default selected. I tried settin ...

JS - Reducing in size increases request size

I'm facing an issue with compressing my request - instead of reducing the size, it seems to be increasing it: const requestData = LZString.compress(JSON.stringify({ data: bigBase64StringHere })); await axios.post("api-endpoint", requestData, ...

"Revolutionize Your Site with Endless Scrolling using q

I am currently in the process of developing a web application using create-react-app along with the packages Infinite-Scroller and qwest. (https://www.npmjs.com/package/react-infinite-scroller) (https://www.npmjs.com/package/qwest) This is how my code l ...

Oops! The program encountered an issue on the production environment, but it's running smoothly

When I execute Webpack using the command node node_modules/webpack/bin/webpack. js --env. prod An error message is displayed below. However, when running in --env. dev mode, the command executes without any issues. Can't resolve './../$$_gen ...

JavaScript - Modifying repeating numbers in an array

I have a list of numbers that repeats multiple times like this; numbers = [2,3,1,2,1,3,3,1,2] My goal is to increase each repeated number by 10 every time it appears. Therefore, the updated list should look like this; updated_numbers = [2,3,1,12,11,13,23, ...