vscode Intellisense NodeJs - javascript

In vscode, developers have the ability to hover over methods and properties of variables and objects in our code, and it will show you information regarding them. Unfortunately, that is lost once we pass the code to a module inside of another file (since javascript is statically typed). Is there any way possible for me to explicitly type a parameter passed to a module? Or maybe a source-map of some sort? An example of where I have an issue:
app.js
var express = require('express')
var app = express()
var routes = './routes/route.js'
route.js
module.exports = function(app) {
// Hovering over app doesn't show the intellisense like it does in app.js
}
Update
I have continued searching for the answer but have not found one. This is the closest to getting it to work I have got, but for some reason, the editor doesn't want to apply the type. See below:
route.js
import Express from 'Express'
/**
* #param {Express} app
*/
module.exports = function(app) {
// Hovering over app doesn't show the intellisense like it does in app.js
}

...since javascript is statically typed.
JavaScript not statically typed. It is dynamically typed.
Is there any way possible for me to explicitly type a parameter passed to a module?
Yes. Use TypeScript. It provides optional static typing.
Explanation
In the following screenshot, the Intellisense does work. It is showing that app is of type any. That is the best we can do with JavaScript, because JavaScript is dynamically typed. At compile time, app can indeed be any type. Being dynamically typed means that the type is checked only at runtime.

The first problem is that VS Code has no way to determine that app is actually of the type Express. This is why your second version of route.js seems to make sense.
The problem with that, is that object types are not supported in JSDoc as interpreted by VS Code.
VS Code internally uses the JavaScript Language Service, as shown here. The thing is, the JavaScript Language Service itself is actually TypeScript (see the links to it on the same page).
This answers your question in the following ways:
First, The JSDoc annotations supported by VS Code are the same as those supported by TypeScript. And from the TypeScript documentation, this is not supported:
/**
* #param {object} param1 - Listing properties on an object type does not work
* #param {string} param1.name
*/
function fn7(param1) {}
And that's why your second attempt didn't work.
But this is supported:
/** #type {{a: string, b: number}} */
var var12;
So if you're very adventurous indeed, you could add the most-needed properties that way. I don't think that's even worth the effort.
The final alternative would be to actually use TypeScript. It doesn't need to make a huge difference to your code and will give you the type information you need.
So I created a route.ts that looks like this:
import 'node';
import { Express } from 'Express';
module.exports = function(app: Express) {
// Your code with IntelliSense goes here
}
This preserves the type information and IntelliSense and works like a charm. The trade-off is that you require one more build step (which you can handle transparently in your task runner or with tsc --watch).
Then again, you get the IntelliSense you want without being bound to ES6 (default TypeScript config uses ES5) and without being forced to use any more TypeScript than you want. All the rest of your code can be plain JavaScript if you feel like it.
To recap, your three alternatives are:
No IntelliSense.
Type annotations that explicitly list needed properties or functions.
Use TypeScript type annotations.
Edit: I should add that I also installed the TypeScript type imports for both Node and Express. I'm not sure if they are explicitly needed, but you should probably install them if you want to go this route.
Use this:
npm install #types/node
npm install #types/express

There is a really useful feature in VS Code that enables Synthetic Default Imports. It will automatically import typing information from the JSDoc or from type definition files.
I've linked a blog post that shows you both how to get setup, and a practical example of the functionality it

I think a better solution would be this:
function myFunc() {
//damn code
}
export default myFunc
As you can see, the function is not wrapped, but requires es6 ;)

Related

How to create hints for personal functions in VS Code

I want to make a personal library with all my js functions written.
What I really need is that I need the intellisense of VS Code to show me more hints about this functions - type of the input variables, type of the return, maybe some information about the function.
For example - a hint for substring looks like this:
and a hint for my function looks like this:
Digging a bit in VS Code I was able to find, that there are .ts files with declaration of objects, interfaces and functions (for example declaration of substring() in lid.es6.d.ts, etc.)
My first thought is whether I can make a personal .ts file with declaration of all my functions and make the intellisense refer to it too.
You don't need a .d.ts for your javascript functions, you can just use a jsDoc comment. Ex:
/**
* Does stuff
* #param {Number} params
*/
function myFun(params) {
}
Between {} is the type of the parameter. You can also add a description to your function.
VsCode will pick-up JsDoc comments and use them to show you hints. Also if you type /** and then press tab above a function it will insert a property formatted jsDoc comment base on your function to give you a starting point.
JsDoc reference here
Note: Give typescript a try, you will get the nice hints you are looking for because Typescript allows you to specify the argument types, also the output of typescript is plain TS, so you will be able to use your library anywhere you could have used it before
Yes you could write your own typings. If you don't want to use TS, you can use the JSDoc standard:
/**
* Get a survey's details and possible answers by id
* #param {Number} surveyId
*/
const getById = async (surveyId) => {
...
}
VSC is smart enough to read all that and display it when implementing.
Especially when you build services which are exported and the imported this works great.
JSDoc info: http://usejsdoc.org/about-getting-started.html

Require JS Object and Functions in Typescript Angular2 project

I am working with Typescript 2.1.5 and trying to write some newer sections of code with it.
I have a JS library that i am not ready to update in Typescript. The problem is that i need to consume some of that Javascript code in a simple Typescript component.
I have been fetching the web for a way to get my Javascript functions to get called and work in a .TS file. Unfortunatly informations are either outdated(mostly), not clear or not explained at all. I allready tryed a whole lot of tricks but none works.
So my question is : What is the easiest way and simplest way to consume javascript code in a TS file (ES6) ? There must be a protocol to follow to get this done.
Any tutorial, link or explanations will be much appreciated and i'am sure will help others in the same situation.
For better understanding here are some code snippets :
I start by doing an import of my JS file in the component.ts
var ZP = require('./zp.js');
In my js file i have two types of functions. Respectively a basic one and another which are methods from an object:
**export** function logTest(){
console.log("Responding");
}//Simple function
(*i just found while editing, that by appending the keyword "export" to the simple function makes it avaible to call in TS file. Wich doesn't work with methods *).
DObject.stringtest = function(){
return "Responding";
} //Object Method
Back to my component.ts file i call each one. The simple function with the export keyword now gets called, but the DObject method remains undefined.
(I made dummy calls for the DObject just to show what doesn't work even if it can seem obvious to some).
testFunc(event){
console.log("event is okay");
console.log(ZP.logTest()); <--- Now Works
console.log(ZP.stringTest()); <--- Don't Work
console.log(ZP.DObject.stringTest()); <--- Don't Work
console.log(ZP.DObject.stringTest); <--- Don't Work
}
Here are the console logs :
DObject.stringTest is not a function
( For the first two calls to stringTest() )
Undefined
( For the last call to DObject.stringTest )
( I just found something about Declaration files, i ll give it a shot, hopefully if it works i ll edit a full explained step by step tutorial. In the mean time any help would be much appreciated. )
In your case your existing javascript files are not detected in typescript because typings are not available.
you can create a type definition (index.d.ts) file and keep adding your typings so that it will be available to your tsc compiler.
Below are few thing you need to understand when coming to the typescript world. Some of you might know, some may not.
every typescript file transpiled to java script into specific version you want in tsconfig.json
typescript understand typescript ( for javascript type definition is required) or type definition.
there are many typing available in 2 forms :
typings install
#types namespace package
import and export makes available your members and properties to others

How to override library models that belong to app domain in Typescript [Module augmentation]?

I currently use two applications with the same code for models.
I would like to create and share a library model (Typescript) which uses inheritance.
Example : Pet extends Author.
In my current application (Angular, the new one) I need to add some prototypal functions that belong in the app domain to my both classes Pet and Author.
Example :
pet.extension file
Pet.prototype[‘fetchUrl’]
author.extension file
Author.prototype[‘follow’]
So I create a file named pet.extension which imports pet.class where I add domain methods then I export and use these overloaded classes.
When I create a new instance I import this extension. No problem. I have my class (Pet) with extended prototypal methods. However this extension(overloaded class) uses pet.class and pet.class extends author.class not author.extension
Do you know any way to create and use an overloaded version of Author?
The ultimate goal in my mind would be to simulate an equivalent of extensions in Swift language. To keep in my library the oop structure.
I hope I’m clear :)
Thanks a lot for your help.
Relative to my previous post, I finally found the exact term of what I am trying to do. A module augmentation.
import { Model } from ‘my-library’;
declare module ‘my-library’ {
interface Model {
newMethod(): string
}
}
Model.prototype.newMethod = function() {
return ‘something’;
}
Here is an example. Visual studio code debugger returns the following error on Model from "Model.prototype.newMethod"
‘Model' only refers to a type, but is being used as a value here.
I tried to export my library from commonjs and amd, but getting the same issue.
So I tried to reproduce the same code available on Typescript
documentation.
This time, no error displayed on Model Object but on assignation method :
'Property 'newMethod' does not exist on type ‘Model’.
I’m using typescript 2.1.6 for my library and 2.0.3 with angular.
It begins to drive me insane. Any ideas of what's wrong ?
Thanks a lot for your help.

VSCode AngularJS intellisense

So with the new language service Salsa nearing completion (Available in 1.8.0-insider with a few environment variables set) You can get intellisense support with jsDoc comments. This is thanks to TypeScripts support for it.
However my issue is continuing that into an AngularJS service, or controller for custom types. Just to note this is for JavaScript code, not TypeScript.
Let me quickly demonstrate my setup to hopefully explain.
I define my services, controller etc as CommonJS modules (To enable framework independent code reuse, and easier organisation)
//service.js
/**
* #typedef {Object} someObj
* #prop {int} value
*/
/**
* Do some action
* #param {string} val
* #returns {someObj}
*/
exports.test = function(val) {
return {};
}
When i require this anywhere in my project VSCode picks up the JSdoc comments and gives me correct intellisense, brilliant.
So traditionally i would plug that into angular like:
angular.module('myModule').service('service', () => require('./service'));
Now is where my wonderful new intellisense dreams are crushed.
Understandably when i inject that service anywhere all intellisense is lost as VSCode nor TypeScript can be expected to understand the intricacies or every frameworks DI systems.
So I'm left a little stumped.
I had the thought of creating typing's just for the angular wrappers around my services, controllers. However upon researching it i couldn't find anyway of getting the types defined in the world of JSDoc into my definition files (I'm not a TypeScript guy).
I also had the thought of using #module syntax of jsDoc to define all my CommonJs services, controllers etc. Then using that module to set the type of each function parameter where I'm injecting in my custom modules, However I'm not entirely sure that would work looking at the docs. It seems you have to specify your #module tag with the path expected in your require call which wouldn't work with relatively required modules where that path would change. Nor does the docs mention being able to use it in the #Param tag to say this variable is this module.
The other option i considered was some extension that helps VSCode's intellisense by providing the missing link. Consume the known intellisense info when defining the service and passing it onto the instances where that service is injected. However i couldn't find any such extension, nor even if it would be possible to create.
With the advances in VSCode's intellisense thanks to Salsa and typescript is this at all possible? I had got my hopes up with the JSDoc support, but i guess too soon.
Any help would be greatly appreciated, that or any workarounds where i could change my way of working slightly to get the desired result.

TypeScript use typescript-require shared files

I use TypeScript to code my javascript file with Object Oriented Programing.
I want to use the node module https://npmjs.org/package/typescript-require to require my .ts files from other files.
I want to share my files in both server and client side. (Browser) And that's very important. Note that the folder /shared/ doesn't mean shared between client and server but between Game server and Web server. I use pomelo.js as framework, that's why.
For the moment I'm not using (successfully) the typescript-require library.
I do like that:
shared/lib/message.js
var Message = require('./../classes/Message');
module.exports = {
getNewInstance: function(message, data, status){
console.log(requireTs);// Global typescript-require instance
console.log(Message);
return new Message(message, data, status);
}
};
This file need the Message.js to create new instances.
shared/classes/Message.ts
class Message{
// Big stuff
}
try{
module.exports = Message;
}catch(e){}
At the end of the fil I add this try/catch to add the class to the module.exports if it exists. (It works, but it's not really a good way to do it, I would like to do better)
If I load the file from the browser, the module.export won't exists.
So, what I did above is working. Now if I try to use the typescript-require module, I'll change some things:
shared/lib/message.js
var Message = requireTs('./../classes/Message.ts');
I use requireTs instead of require, it's a global var. I precise I'm using .ts file.
shared/classes/Message.ts
export class Message{
// Big stuff
}
// remove the compatibility script at the end
Now, if I try like this and if I take a look to the console server, I get requireTs is object and Message is undefined in shared/lib/message.js.
I get the same if I don't use the export keyword in Message.ts. Even if I use my little script at the end I get always an error.
But there is more, I have another class name ValidatorMessage.ts which extends Message.ts, it's not working if I use the export keyword...
Did I did something wrong? I tried several other things but nothing is working, looks like the typescript-require is not able to require .ts files.
Thank you for your help.
Looking at the typescript-require library, I see it hasn't been updated for 9 months. As it includes the lib.d.ts typing central to TypeScript (and the node.d.ts typing), and as these have progressed greatly in the past 9 months (along with needed changes due to language updates), it's probably not compatible with the latest TypeScript releases (just my assumption, I may be wrong).
Sharing modules between Node and the browser is not easy with TypeScript, as they both use very different module systems (CommonJS in Node, and typically something like RequireJS in the browser). TypeScript emits code for one or the other, depending on the --module switch given. (Note: There is a Universal Module Definition (UMD) pattern some folks use, but TypeScript doesn't support this directly).
What goals exactly are you trying to achieve, and I may be able to offer some guidance.
I am doing the same and keep having issues whichever way I try to do things... The main problems for me are:
I write my typescript as namespaces and components, so there is no export module with multiple file compilation you have to do a hack to add some _exporter.ts at the end to add the export for your library-output.js to be importable as a module, this would require something like:
module.exports.MyRootNamespace = MyRootNamespace
If you do the above it works, however then you get the issue of when you need to reference classes from other modules (such as MyRootNamespace1.SomeClass being referenced by MyRootNamespace2.SomeOtherClass) you can reference it but then it will compile it into your library-output2.js file so you end up having duplicates of classes if you are trying to re-use typescript across multiple compiled targets (like how you would have 1 solution in VS and multiple projects which have their own dll outputs)
Assuming you are not happy with hacking the exports and/or duplicating your references then you can just import them into the global scope, which is a hack but works... however then when you decide you want to test your code (using whatever nodejs testing framework) you will need to mock out certain things, and as the dependencies for your components may not be included via a require() call (and your module may depend upon node_modules which are not really usable with global scope hacking) and this then makes it difficult to satisfy dependencies and mock certain ones, its like an all or nothing sort of approach.
Finally you can try to mitigate all these problems by using a typescript framework such as appex which allows you to run your typescript directly rather than the compile into js first, and while it seems very good up front it is VERY hard to debug compilation errors, this is currently my preferred way but I have an issue where my typescript compiles fine via tsc, but just blows up with a max stack size exception on appex, and I am at the mercy of the project maintainer to fix this (I was not able to find the underlying issue). There are also not many of these sort of projects out there however they make the issue of compiling at module level/file level etc a moot point.
Ultimately I have had nothing but problems trying to wrestle with Typescript to get it to work in a way which is maintainable and testable. I also am trying to re-use some of the typescript components on the clientside however if you go down the npm hack route to get your modules included you then have to make sure your client side uses a require compatible resource/package loader. As much as I would love to just use typescript on my client and my server projects, it just does not seem to want to work in a nice way.
Solution here:
Inheritance TypeScript with exported class and modules
Finally I don't use require-typescript but typescript.api instead, it works well. (You have to load lib.d.ts if you use it, else you'll get some errors on the console.
I don't have a solution to have the script on the browser yet. (Because of export keyword I have some errors client side) I think add a exports global var to avoid errors like this.
Thank you for your help Bill.

Categories