How to call JavaScript from TypeScript? - javascript

I have a javascript file that I want to call from typescript. I fixed one import problem and modified the base function to be recognized in tsc, however, I'm still facing an issue recognizing a declared function prototype in the javascript file.
I do have "allowJs": true.
Here is my fileTransfer.ts:
import { XmlRpcRequest } from "./mimic";
const updateCommentBtn: HTMLButtonElement = document.getElementById(
'makeComment',) as HTMLButtonElement;
updateCommentBtn.addEventListener('click', async () => {
const method = "MakeComm";
let request:any = XmlRpcRequest("http://localhost:1337/RPC2", method);
request.addParam(document.getElementById("n1")).value;
request.addParam(document.getElementById("n2")).value;
let response = await request.send();
console.log(response);
});
And here are the relevant portions of the mimic.js file that I'm importing:
export const XmlRpcRequest = (url, method) => {
this.serviceUrl = url;
this.methodName = method;
this.crossDomain = false;
this.withCredentials = false;
this.params = [];
this.headers = {};
};
XmlRpcRequest.prototype.addParam = (data) => {
// Vars
var type = typeof data;
switch (type.toLowerCase()) {
case "function":
return;
case "object":
if (!data.constructor.name){
return;
}
}
this.params.push(data);
};
tsc compiles the project and the linter does not flag any errors. But, I get the following error in Chrome's console:
mimic.js:8 Uncaught TypeError: Cannot set property 'addParam' of undefined
This seems to me like an issue with accessing the exported function's prototype but I'm not quite sure how to fix it. I should mention that I can run the file just fine in a Javascript only application, I only face this issue going to the Typescript environment.

Here's an answer why you're unable to use fat arrow syntax if you want to access prototype:
https://teamtreehouse.com/community/does-arrow-function-syntax-works-for-prototype
Here's two additional explanations about this with fat arrow syntax:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Use_of_prototype_property
As a resolution you need to define it with normal function declaration:
const XmlRpcRequest = function(url, method) { ... }
Alternatively, you could use class:
class XmlRpcRequest {
constructor(url, method) {
...
}
}

Related

Why is function wrapper called with module.exports instead of module?

Snipper from source code
Like the answer below says:
https://stackoverflow.com/a/22771432
When JavaScript files are require()d as Node modules, the Node engine runs the module code inside of a wrapper function. That module-wrapping function is invoked with a this set to module.exports
This is what I don't understand, why calling it module.exports rather than just module?
The wrapped function is passed module.exports as this keyword but why don't we just pass module
You can look at the loader code yourself here. Where the this value for a module is set is actually here.
I can't say why they decided it should be the exports object rather than module object. Perhaps because that's what they expect you to assign to and it's certainly more common to reference exports than it is module.
Both the module object and the exports objects are passed as an argument to the wrapper and are in scope for your code so you can reference either one without using this and, in my opinion, it makes for clearer code to use module or module.exports directly rather than using this.
Here's a copy of the relevant code from the above link. The this value is set in the ReflectApply() call near the bottom of this block in the variable named thisValue.
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
let moduleURL;
let redirects;
if (policy?.manifest) {
moduleURL = pathToFileURL(filename);
redirects = policy.manifest.getDependencyMapper(moduleURL);
policy.manifest.assertIntegrity(moduleURL, content);
}
maybeCacheSourceMap(filename, content, this);
const compiledWrapper = wrapSafe(filename, content, this);
let inspectorWrapper = null;
if (getOptionValue('--inspect-brk') && process._eval == null) {
if (!resolvedArgv) {
// We enter the repl if we're not given a filename argument.
if (process.argv[1]) {
try {
resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
} catch {
// We only expect this codepath to be reached in the case of a
// preloaded module (it will fail earlier with the main entry)
assert(ArrayIsArray(getOptionValue('--require')));
}
} else {
resolvedArgv = 'repl';
}
}
// Set breakpoint on module start
if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) {
hasPausedEntry = true;
inspectorWrapper = internalBinding('inspector').callAndPauseOnStart;
}
}
const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new SafeMap();
if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, thisValue, exports,
require, module, filename, dirname);
} else {
result = ReflectApply(compiledWrapper, thisValue,
[exports, require, module, filename, dirname]);
}
hasLoadedAnyUserCJSModule = true;
if (requireDepth === 0) statCache = null;
return result;
};

How to exclude a custom constructor in webpack treeshaking?

I have the following code in a JS file
(() => {
const Abc = (ab) => {
this.ab = ab;
this.k = () => {
console.log(this.ab);
};
};
window.MySpace = window.MySpace || {};
window.MySpace.abc = new Abc('some var');
})();
I'm using webpack 5 as my bundler. In another file that loads after this constructor, when I tried using window.Myspace.abc.k, it threw an error. With a little investigation, I'm able to understand that the output file does not have the k, as a result of TreeShaking mechanism.
How do I tell webpack to exclude this constructor/method during treeshaking?
window.MySpace.abc = new Abc('some var');
Abc is an arrow function. Arrow functions cannot be used as a constructor, so this line of code is throwing an exception, and thus nothing gets assigned to window.MySpace.abc.
To fix this, use a regular function:
function Abc(ab) {
this.ab = ab;
this.k = () => {
console.log(this.ab);
};
};

Modifying some features in a public npm package

I am trying to overload/replace functions in the ami-io npm package. That package is created to talk to asterisk AMI, a socket interface.
I need to talk to a service that has almost the exact same interface, but it presents a different greeting string upon login, and it requires an extra field in the logon. All the rest is the same. Instead of just plain copying the 600 LOC ami-io package, and modifying two or three lines, I want to override the function that detects the greeting string, and the login function, and keep using the ami-io package.
Inside the ami-io package there is a file index.js which contains the following function:
Client.prototype.auth = function (data) {
this.logger.debug('First message:', data);
if (data.match(/Asterisk Call Manager/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new Action.Login(this.config.login, this.config.password), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
Now I want to match not on Asterisk Call Manager, but on MyService, and I want to define and use Action.LoginExt(this.config.login, this.config.password) with another one with an extra parameter.
Is this possible? I tried this in my own module:
var AmiIo = require('ami-io');
var amiio = AmiIo.createClient({port:5038, host:'x.x.x.x', login:'system', password:'admin'});
amiio.prototype.auth = function (data) {
this.logger.debug('First message:', data);
if (data.match(/MyService Version/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new Action.LoginExt(this.config.login, this.config.password, this.config.extra), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
...but it resulted in TypeError: Cannot set property 'auth' of undefined, and now I am clueless.
Also, can I define a new Action.LoginExt object in my own module? How?
The action.js module defines the Action objects as follows:
function Action(name) {
Action.super_.bind(this)();
this.id = this.getId();
this.set('ActionID', this.id);
this.set('Action', name);
}
(function(){
var Message = require('./message.js');
var util = require('util');
util.inherits(Action, Message);
})();
Action.prototype.getId = (function() {
var id = 0;
return function() {
return ++id;
}
})();
function Login(username, secret) {
Login.super_.bind(this, 'Login')();
this.set('Username', username);
this.set('Secret', secret );
}
... more functions ...
(function() {
var actions = [
Login,
... more functions ...
];
var util = require('util');
for (var i = 0; i < actions.length; i++) {
util.inherits(actions[i], Action);
exports[actions[i].name] = actions[i];
}
exports.Action = Action;
})();
What I think I understand is that Action is subclassed from Message. The Login function in its turn is subclassed from Action, and exported (in the last code block).
So I think in my code I could try something similar:
// extend ami-io with LoginExt function
function LoginExt(username, secret, company) {
Login.super_.bind(this, 'LoginExt')();
this.set('Username', username);
this.set('Secret', secret );
this.set('Company', company);
}
var util = require('util');
util.inherits(LoginExt, amiio.Action);
But util.inherits fails with undefined. I've also opened a issue on ami-io.
You can use:
var AmiIo = require('ami-io');
AmiIo.Action.Login = function NewConstructor(){}; //to override Login action
//new constructor shold extend AmiIo.Action.Action(actionName)
//and also, you can use
AmiIo.Action.SomeNewAction = function SomeNewAction(){};//to create new actuion
//it also should extend AmiIo.Action.Action(actionName);
AmiIo.Action is just an Object. All constructors are fields of it.
To create new events you don't need to do anything, because it is just an object. If server send to you
Event: Armageddon
SomeField: 123
ami-io will create event with name 'Armageddon'.
To override Client#auth() method, you just should do
var AmiIo = require('ami-io');
AmiIo.Client.prototype.auth = function (){};//new function
amiio is an instance of a Client. The prototype property is only meaningful on constructor functions, such as Client. It is not meaningful on the result of a constructor function (except in the uncommon case that the instance happens to also be a function itself -- but even in that case, altering the instance's prototype does not influence its parent constructor).
Instead, you need to get the instance's prototype with Object.getPrototypeOf:
Object.getPrototypeOf(amiio).auth = function() { ... }
If you don't need to change this for every client, but only a single client, you don't need to change the prototype at all. Changing the instance's auth is sufficient:
amiio.auth = function() { ... }
Note that you code will not work if Action.LoginExt is local to the module scope. If the module exports it, you can probably do AmiIo.Action.LoginExt instead. If it does not export LoginExt, you will need to copy the code that implements it a re-implement it in your importing scope. It may be simpler to modify the module itself.
Here's the solution I applied that worked:
// Override the AmiIo auth procedure, because the other login is slightly different
// Write our own Login function (which adds a company)
function Login(username, secret, company) {
Login.super_.bind(this, 'Login')();
this.set('Username', username);
this.set('Secret', secret );
this.set('Company', company);
}
// This function should inherit from Action
var util = require('util');
util.inherits(Login, AmiIo.Action.Action);
AmiIo.Action.Login = Login;
// replace the auth with our own, to add the company. Also
// this sends a slightly different greeting: "Service Version 1.0"
AmiIo.Client.prototype.auth = function (data) {
if (data.match(/Service Version/)) {
this._setVersion(data);
this.socket.on('data', function (data) {
this.splitMessages(data);
}.bind(this));
this.send(new AmiIo.Action.Login(this.config.login, this.config.password, this.config.company), function (error, response) {
if (response && response.response === 'Success') this.emit('connected');
else this.emit('incorrectLogin');
}.bind(this));
} else {
this.emit('incorrectServer', data);
}
};
// our own function to grab the version number from the new greeting
AmiIo.Client.prototype._setVersion = function(version){
var v = version.match(/Service Version ([\d\.]*[\-\w\d\.]*)/i);
if (v){
this.version = v[1];
}
};
So it turns out this was as doable as I hoped it would be. Both answers by #NumminorihSF and #apsillers helped me here, but I could mark only one of them as the best answer.

'TypeError: is not a function' in Node.js

I'm getting the error while running the following code in Node.js
var assert = require('assert');
var request = require('request');
var index = require('./index');
it('verify javascript function', function(done) {
var v2 = index.AddNumbers(5, 6);
assert.equal(11, v2);
done();
});
The index.js file contain the following code:
function AddNumbers(a,b){
return a+b;
}
What am I doing wrong?
This happened to me many times because of circular dependency, check if you have 2 classes that are requiring each other, remove one of them from requiring the other and the issue should be solved
With NodeJS modules, to make something public, you have to export it. Add this to the end of index.js:
module.exports.AddNumbers = AddNumbers;
(That's using the old CommonJS modules. For ESM, it would be export AddNumbers;)
Here it is running on my machine:
$ cat index.js
function AddNumbers(a,b){
return a+b;
}
module.exports.AddNumbers = AddNumbers;
$ cat example.js
var index = require('./index');
var v2 = index.AddNumbers(5,6);
console.log(v2);
$ node example.js
11
I'm fairly a beginner at Node JS so I managed to get this error by importing a function like so:
const { functionName } = require('./function')
instead of like so:
const functionName = require('./function')
Editing my post to add an explanation since I've learned more node since I wrote it. If a module exports an object containing multiple functions functions like so:
module.exports = { functionName, otherFunction }
Then the function has to be deconstructed out of the object during the import, as in the first code snippet. If the module exports a single function or a default function, like so:
module.exports = functionName
Then tt must be imported directly, as in the second code snippet.
If you need to expose a specific component, function or a variable to public. You have to exports those components using JavaScript modules.
let add = (a,b)=>{
return ( a+b);
}
module.exports.add=add;
or if you want to expose multiple functions, you can do as follows.
let add = (a,b)=>{
return (a+b);
}
let subtract = (a, b)=>{
return (a-b);
}
module.exports={
add : add,
subtract : subtract
};
This is happening because two files are referencing each other i.e You are calling function (s) from file A in file B and vice versa which is called Circular Dependency.
Your "AddNumbers" function in the "index.js" file should be as follows,
function AddNumbers(a,b){
var addition = function(a, b){
return (a + b) ;
};
module.exports = {
additionResult: addition
};
}
And you need to call it in your "Node.js" file as follows
var assert = require('assert');
var request = require('request');
var index = require('./index');
it('verify javascript function', function(done) {
var v2 = index.additionResult(5, 6);
assert.equal(11, v2);
done();
});
This should work. Please note that you call the function by whatever the token name you exported the return value by (I use a different name here just for clarity). Almost everybody uses the same name as the function name so there are no confusion. Also in ES6, if you use the same name you can export as just,
module.exports = {
addition
};
instead of,
module.exports = {
addition: addition
};
since you use the same name. It is an ES6 feature.
I ran into the same problem while trying to follow a Nodejs tutorial by w3schools.
I copied the following code from them:
exports.myDateTime = function () {
return Date();
};
That, however, wouldn't work for me. What resolved the problem for me was adding module. before the exports keyword like this:
module.exports.myDateTime = function () {
return Date();
};
The most correct answer was from #shimi_tap. I want to reply it as comment, but doesn't have enough reputation, so I am gonna answer it using a simple example, like in this case below:
File A has 3 functions to process database activity: function
addDB, updateDB, and delData;
File B has 2 functions to process User activity on smartphone:
function addHistory, and editHistory;
Function updateDB in file A is calling function editHis in file B, and function editHistory is calling function updateDB in file A. This is what we called circular-dependency. And we need to prevent it by only giving output of state from editHistory and the rest will be processed inside file A.
//ORIGINAL FUNCTIONS which caused CIRCULAR DEPENDENCY
function updateDB() {
//process update function here
//call function in fileB
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
response = {
state: 1,
message: "success",
};
}
});
return response;
}
//THIS is the WRONG ONE
function editHistory() {
//process function to edit History here
//call function in fileA
const file = require("fileA.js");
await file.updateDB(data).then((output) => { //You should not call it here
if(output["message"] === "success") {
output = {
state: 1,
message: "success",
};
}
});
return output;
}
//==================================================//
//THE FIX
function updateDB() {
//process function here
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
await updateDB(data).then((output) => {
response = {
state: 1,
message: "success",
};
});
} else {
log("Error");
}
});
return response;
}
function editHistory() {
//process function to edit History here
// No more calling to function inside the file A
output = {
state: 1,
message: "success",
};
return output;
}
https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
this post visualizes the circular dependency injection
like a child or nested file tried to import parent or top-level file
repo.js
service.js
there are 2 files
service.js uses repo.js file by importing
it works
but check in repo.js that it tried to import service.js file
it shows circular dependency injection warning
In my case the problem was the missing semicolon at the end of the lines.
const codec = JSONCodec()
(async () => {
for await (const message of subscription) {
const payload = codec.decode(message.data)
stompServer.send('/topic/update-event', {}, payload)
}
})()
This produced the following error:
TypeError: JSONCodec(...) is not a function
I was so used to writing code without semicolons, which led me to this problem with the bare NodeJs. As soon as I had put the semicolons, the problem disappeared.
A simple way I debugged this (After about 2 days of troubleshooting) was to actually see why 'x' is not a function. Basically, console.log(x) to see the actual object returned. Turned out I was conflicting x with another declared variable (happens especially when you use axios.res and req,res args.
Require the other file in function level.
fileOne.js
function main() {
const fileTwo = require('./fileTwo');
console.log("hello from file one");
}
module.exports = main;
main();
fileTwo.js
function main() {
const fileOne = require('./fileOne');
console.log("hello from file two");
}
module.exports = main;
main();
Now execute > node fileOne.js
Output:
hello from file two
hello from file one
One silly mistake I did was while exporting was:
module.exports = [module_name_1, module_name_2, ..., module_name_n]
The right way is:
module.exports = {module_name_1, module_name_2, ..., module_name_n}

How to create javascript function constructor with default functionality and persistent properties?

I've got a slightly unusual pattern I'm trying to achieve and have not quite figured it out. My goal is to create a function called debugLog as a flexible console.log replacement, which can be called as follows:
debugLog('thing to log #1', 'thing to log #2', objectToLog1, objectToLog2);
^^ the number of params should be arbitrary, just as is possible with console.log
That is what I'll call the "default" functionality. Now I'd also like to add some additional functionality through property functions.
Examples:
debugLog.setDebugFlag(true); // sets the internal this.debugFlag property to true or false depending on param
I'm trying to do this in Node and what I have so far does not quite let me achieve this pattern:
var debugLog = function () {
this.debugFlag = this.debugFlag || true;
if (this.debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
debugLog.prototype.setDebugFlag = function (flagBool) {
this.debugFlag = flagBool;
}
module.exports = new debugLog();
This module would be including in a Node app using the standard require pattern:
var debugLog = require('./debugLog.js');
The question is how can I achieve this pattern of a function object with default functionality, but also extended "property" style functions? Please note I am already aware of the typical pattern where ALL functionality comes from function properties (such as debugLog.log() and debugLog.setDebugFlag()). But that patterns does NOT achieve my key goal of a shorthand for the default functionality that simply involves calling the function directly.
Is it even possible to do?
You could do it this way
var debugLog = (function() {
var debugFlag = true;
function log() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
log.setDebugFlag = function(flag) {
debugFlag = flag;
}
return log;
})();
module.exports = debugLog;
You could use closure, like so:
// debugLog.js
var debugFlag = true;
function debugLog() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
}
debugLog.setDebugFlag = function (newFlag) {
debugFlag = newFlag;
}
module.exports = debugLog;
and use it like this:
// otherFile.js
var debugLog = require('./debugLog');
debugLog('Hey!');
debugLog.setDebugFlag(false);
debugLog('This wont appear!');

Categories