JSDoc to handle Rhino's importPackage - javascript

In Rhino, one can do
importPackage(Packages.foo.bar.xyzzy);
This imports in the current namespace all the members of the Java foo.bar.xyzzy class.
I've already set up Typescript declaration files for that class so I can use
var x = Packages.foo.bar.xyzzy.memberA;
var y = Packages.foo.bar.xyzzy.memberB;
and get completion, type checking etc in VSCode (and the typescript that it uses)
With that importPackage, I can use
var z = memberA;
and it works (at run time) but VSCode complains because it has no idea that importPackage means that the members are imported.
Can I add some JSDoc that will cause VSCode to know that? It's a sort of
import * from Packages.foo.bar.xyzzy
I guess
EDIT:
class xyzzy extends Packages.foo.bar.xyzzy {}
gets me close, but two problems: Rhino doesn't support class (so I need it to be in a JSDoc comment instead) and it means I can do xyzzy.methodY() now, but not just methodY()
My typescript-fu isn't that strong so I'm not really sure how to go about this

I'm afraid, there is no way to do what you want via typescript + jsdoc.
importPackage works like a with statement. It modifies current execution context. TypeScript itself doesn't support types for with. Also, JavaScript doesn't have such conception as import * from "foo" (The correct one import * as foo from "foo").

Related

UglifyJS - Mangle functions but preserve Function.prototype.name

When using UglifyJS, function names are mangled, unless keep_fnames is set to true. For example, the following Typescript code:
class Test {}
console.log(Test.name);
compiled to JS as:
function Test() {}
console.log(Test.name);
will be uglified to:
function t() {}
console.log(t.name);
and output t instead of test to the console.
Is there a way (other than using keep_fnames option) to preserve the name property after uglification ? (I don't want to use keep_fnames:true because it increases the bundle size quite a lot.
Possible solutions I thought of:
Writing a Webpack plugin that hard codes the function name Test.name = 'Test', but this won't work as Function.prototype.name is a read only property;
Using Typescript decorators, metadata and the reflection API, but design:type metadata is not emitted for classes, it's only emitted for properties (which I believe is because Function.prototype.name exists, but I guess they missed this edge case ?).
As it's explained here, Function.prototype.name cannot be relied on in client-side code, because the information on function original name will be destroyed with minification process. Preventing it from being renamed is quick-and-dirty fix.
name is read-only and non-configurable in some browsers, so doing something like
class Test {
static get name() {
return 'Test';
}
}
or
function Test() {}
Object.defineProperty(Test, 'name', { configurable: true, value: 'Test' });
will fix it in most browsers but result in obscure compatibility problems in rest of them (for example, Android 4.x browser).
The proper way to do this is to never rely on name in client-side code for anything but debugging. As for Node.js and Electron, it depends on whether the code needs to be obfuscated.
If string identifier should exist for a class or a function, another static property name can be picked, e.g. id or unsupported yet conventional displayName.
Is there a way (other than using keep_fnames option) to preserve the name property after uglification...
The only mechanism to keep the correct name involves that name being in the output file, so the short answer is no. If you want to use prototype.name you need to leave that name be.
The alternatives would involve either:
Adding an additional property containing the name, which could introduce errors and would still take up space in your file
Finding a tool that will pre-compile all uses of prototype.name with the string value... I'm not aware that one exists but you never know!

Correct use of the JavaScript interface keyword

First of all, no, I'm not trying to create any sort of Java-like interface for my JavaScript code. I've seen those questions all over, and while I'm still a relative novice to JavaScript, I know those aren't part of the language.
However, I'm curious what the actual intended use of the interface keyword is. For example, Math is an interface, containing definitions (but not implementations). I believe (and may be totally wrong) that these are there to provide a means for the definers of the language to enforce a set of behaviors to be implemented in various JavaScript engines. Is that correct?
Furthermore, I have a desire to have a "static class" that contains a bunch of utility methods. I like that Math.sqrt(3) has an outer namespace ('Math') which is capitalized, and a number of logically similar methods and values in it. Maybe it's just my Java/Ruby background that makes me want a capital on the grouping objects. Is that bad form?
var ShapeInspections = {
isSymmetrical: function (s) {
// determine if shape is symmetrical
},
numAngles: function (s) {
// return the number of angles
}
}
A purely contrived example, but is it anti-idiomatic to name the "module" this way?
Okay, so as with other answers, you know that the keyword interface has no real use case in Javascript world, yet.
Your Math example made me suspicous that you are talking about a design pattern, called Module Pattern, widely used for scoping Javascript code. There are many ways of making your code modular. For example just like OddDev answered you , the famous Prototype Pattern can embed your code in a modular fashion (just like your Math example). Here is the Revealing Prototype Pattern example with also private variables and functions for additional flexibility:
/* Example from:
http://www.innoarchitech.com/scalable-maintainable-javascript-modules */
var myPrototypeModule = (function (){
var privateVar = "Alex Castrounis",
count = 0;
function PrototypeModule(name){
this.name = name;
}
function privateFunction() {
console.log( "Name:" + privateVar );
count++;
}
PrototypeModule.prototype.setName = function(strName){
this.name = strName;
};
PrototypeModule.prototype.getName = function(){
privateFunction();
};
return PrototypeModule;
})();
but that is not all. Other options include Scoped module pattern, POJO module pattern and many more. Have a look at How to Write Highly Scalable and Maintainable JavaScript: Modules, it has a very simple and yet thorough set of examples.
So far, we talked about plain Javascript. If you have the ability to use libraries in your code, then amazing set of libraries such as Requirejs, CommonsJS are there to help you on this with out-of-the-box functionalities. Have a look at Addy Osmani's post about Writing Modular JavaScript With AMD, CommonJS & ES Harmony.
The interface keyword in javascript is a FutureReservedWord, so it does absolutely nothing right now, though that may change in the future specifications. (See ECMAScript 5.1, section 7.6.1.2). In the ES6 draft, this is also the same.
As for you module, this is a perfectly idiomatic solution. It is always a good idea to "namespace" your functions, as it keeps the global scope as clean as possible.
I believe (and may be totally wrong) that these are there to provide a means for the definers of the language to enforce a set of behaviors to be implemented in various JS engines. Is that correct?
No, this is not correct. Things like "Math" etc. are objects containing functions. If you use for eyample "Math.pow(...)" you just execute the function stored in the "Math" object. Check this example:
var Math = {};
Math.prototype.pow = function(){
alert("stuff");
}
var ShapeInspections = { isSymmetrical: function (s) {
// determine if shape is symmetrical }, numAngles: function (s) {
// return the number of angles } } A purely contrived example, but is it anti-idomatic to name the "module" this way?
It's okay to name your objects like this. As already discussed "Math" is also just an object and follows these naming conventions.
To make things clear for the interface keyword:
The following tokens are also considered to be FutureReservedWords
when they occur within strict mode code (see 10.1.1). The occurrence
of any of these tokens within strict mode code in any context where
the occurrence of a FutureReservedWord would produce an error must
also produce an equivalent error:
implements let private public yield
interface package protected static
It's just reserved cause it's "may" needed in the future. So don't worry too much about it :) http://www.ecma-international.org/ecma-262/5.1/#sec-7.6
Do not confuse the "interfaces" that are specified in IDL with the interface keyword.
The latter is reserved for potential future use, but is not yet actually used in ECMAScript (not even in ES6).

Static import in Javascript without 'with'

Context: I have written a mini JS library for myself which is simply a collection of commonly used classes. I have followed the IIFE (http://benalman.com/news/2010/11/immediately-invoked-function-expression/) technique to separate my code into modules/classes and they're all grouped under a common global namespace var. Let's call it, ns. So we have a typical setup, ns.ClassA, ns.ClassB, etc. Now on the other hand, a separate script (main.js) is loaded at runtime and appended to the document, and this main.js contains the actual code that uses these classes.
Goal: I am trying to find an elegant way of accessing the classes inside main.js directly by calling the class name instead of having to access them through ns. . For example, I would want to be able to do var a = new ClassA(); instead of var a = new ns.ClassA();
Solutions researched & considered:
1) the dreaded 'with' keyword (javascript "static imports"). In this case, I would do something like with(ns){ var a = new ClassA()} , except I will have to wrap the entire main.js inside the with(ns) statement. This is undesirable for obvious reasons.
2) using locally declared variables.
var ClassA = ns.ClassA, ClassB = ns.ClassB;
and then, I will be able to instantiate ClassA and ClassB directly. However, this approach would require me to manually maintain the declaration, and it will just get very messy and hard to maintain as the number of classes increase in the package.
3) pollute the global scope by injection
use a for loop to iterate over ns and map all the classes inside nsto global scope. This is clearly undesirable, and also it will create conflicts for cases such as ns.Event, ns.Audio etc.
4) PaperScript-style (http://paperjs.org/tutorials/getting-started/working-with-paper-js/)
Inspired by PaperScript from PaperJS, where PaperScript code is automatically executed in its own scope that without polluting with the global scope still has access to all the global browser objects and functions, such as document or window. Looking at their source code on GitHub (sorry SO won't let me post any more links), they seem to be using some custom script pre-processing and then Acorn.js to parse. The end result is that one can directly refer to any class inside the paper object. For example, var path = new Path() instead of var path = new paper.Path(), which is exactly what I wanted to achieve. However, my fear is that it might seem to be too much work to implement such a simple feature. So I wanted to see if any one has any ideas?
Thank you for taking your time to read this verbose description of the problem. Any inputs are appreciated.
Note: I have done my best in the past two days into researching this topic, please forgive me if I missed any obvious solutions. In addition, if there's no easy solution to this, I will simply stick with the ns.ClassA pattern. Thank you for your help!
I'm not an expert but I believe you could create a prototype of String and set your vars like so
String.prototype.ns = function(){
return new ns[this]();
}
var ca = "ClassA".ns();

momentjs getting the version number to display on my help about screen

We want to display the version number from momentjs and several other products. I am new to javascript (actually doing almost everything with TypeScript).
I can see there is a VERSION and a moment.version. But I can't seem to access either of them, the VS TypeScript tells me they are visible.
(function (undefined) {
/************************************
Constants
************************************/
var moment,
VERSION = "2.1.0",
round = Math.round, i,
// internal storage for language config files
languages = {},
If you're using the definition from here, it would appear that the version property was left off from the list of static properties. You can easily extend it by including it either in the original definition, or because interfaces in TypeScript are extensible, you can add the missing property by declaring the interface in one of your files with the version property.
interface MomentStatic {
version: string;
}
Because the moment global variable is defined to be the interface MomentStatic in the definition file:
declare var moment: MomentStatic;
You won't need to do anything other than add the version property as shown above.
Now, you can use:
var ver : string = moment.version; // currently 2.5.1 for example

What does google closure's typedef actually do?

I understand what it is for - I can see the benefits of being able to define an alias for a complex type and use that in the documentation. So you can define a type something like...
/** #typedef {{x:number, y:number}} */
example.Point;
...and then use it to document a function, something like...
/**
* #param {example.Point} point
* #return {example.Point}
*/
example.functionThatTakesAPointAndReturnsAPoint(point) {
....
}
But the thing I'm not sure about is, if it's only used for documentation and the compiler's static type checking, then why does the typedef need that line of JavaScript? Couldn't the alias just be defined entirely within the documentation comment block? And, if you served the code directly (without compiling it), what would the JavaScript interpreter do with that line of code after the typedef comment?
The compiler is built on top of Rhino and just enhances the available syntax. I assume it was easier to take the alias from a no-op property access because it matches the standard pattern.
You actually can set a type declaration directly in your method doc:
/**
* #param {{x:number, y:number}} pointLike
*/
var myFn = function(pointLike) {
return pointLike.x + ':' + pointLike.y;
}
alert(myFn({x:34, y:20}))
alert(myFn({x:34, y:'20'})) // will trigger compile time type warning
Benefits of using a #typedef for object types that you use in several places are more in usability/clarity.
For example: maybe you'll get a 'Point.z' attr sometime soon. Using typedef you'd only have to update the typedef declaration and not each inline type declaration in your whole codebase.
See also Using Google Closure's #typedef tag
Its not only for documentation. It gives you also warning when you compile your script using the closure compiler.
The Closure Compiler can use data type information about JavaScript
variables to provide enhanced optimization and warnings. JavaScript,
however, has no way to declare types.
It would have been better if the whole typedef was defined in a comment. Defining part of it as code has some unintended consequences. See this blog post.
The reason the typedef name is defined in the actual code is the way the parsing and typechecking works in Closure Compiler. The parser needs to attach each jsdoc comment to an AST node, otherwise the comment is silently ignored. So, the name of the typedef is defined as code, the jsdoc is attached to it, and later the typechecker picks up the type definition.

Categories

Resources