VSCode IntelliSense - how to declare types - javascript

I'm trying to minimally use TypeScript types in my JS projects so that IntelliSence can provide better completions and I get warnings about type-related problems.
I added a jsconfig.json file that sets up the typechecking in JS, and I mostly use JSDoc to describe the types. This works pretty well.
Now I'd like to create some types that I use throughout my project. I tried using types.d.ts but that seems to be something React-specific and isn't documented.
I also tried putting the types in an index.d.ts file, but that only gets read when you're importing from the index and so it won't be useful for the components that implement what is exported from the index.
/// <reference types="." /> also doesn't import the index.d.ts types into the current scope. I resorted to doing /** #typedef {import('.').Foo} Foo */ for each type I'm using in the current file.
Is there a minimal and maintainable way to manage types in typescript but write the rest in javascript?

If the code in js file is:
const a = prompt();
the code will be
/** #type {number} */
const a = prompt();
Write JSDoc to .js file.

Related

How to use d.ts file for intellisense in VS Code similar to NPM approach

I have a nodejs project (in JS). Unfortunately, I have to utilize a lot of node global variables.
Everything works fine (even a lot of people are suggesting not to use globals) except thing:
There is no intellisense for globals. So every time I want to use, let's say, global function/object I need to look in its code and figure out what are the
parameters, what does it return, etc.
Let's say I have a global variable which is a pure object:
foo = {
bar: {
level2: {
level3: {
level4: "abc
}
}
}
}
It's quite annoying to deal with it since I can't "see" the structure of the object when using it and it's easy to make a mistake when writing code.
The reason why I posted this question is the ...npm packages
There are plenty of packages written in vanilla JS and most of them are utilizing the power d.ts files.
Once you install the package you can use it from any place in your projects and VS code will have intellisense for them. If you will click on tooltip (IDK how it's called... Type definition tooltip?) of VS code
you will be navigated to the d.ts file of the package (not the actual implementation of the command).
So my question is how to do the same in my project. I'm not going to publish it as npm I just want a d.ts file somewhere in the project so I can
use my global without looking into its implementation every time I need to recall what it does.
Let inside your .d.ts file be anything
To access variables, functions, interface add this line in your .ts file, VS code IntelliSense will suggest you
/// <reference path="./test.d.ts" />
If you want to use this test.d.ts all over your project not just on any particular file. Then add this line in tsconfig.json
"files" : [ "./src/test.d.ts" ]
Update as mentioned in the comment section
my js file which I am assuming similar to what you are trying to do
export const testString = 'aditya';
in your js file you
/// <reference path="test.js" />
Regardless of if it is possible or not, the worst drawback of what you are looking for is that the declaration file(s) must be kept updated by hand each time a global variable/function is changed.
There are plenty of packages written in vanilla JS and most of them are utilizing the power d.ts files.
Usually .d.ts files are not written by hand, but are produced by tsc: many of the packages you are speaking about are probably written in TypeScript and distributed as JavaScript packages (to be used in JavaScript projects as well) with an associated index.d.ts file (to be used in TypeScript projects)
even a lot of people are suggesting not to use globals
+1

Mixing JavaScript and TypeScript in Node.js

While having parts of a Node.js in plain ES6, is it possible to mix in some Typescript modules within the same project?
E.g. having some Types defined in TypeScript that are imported via require into plain ES6 files?
Yes this is possible.
Combine the following TypeScript compiler options
--allowJs
Explicitly supports mixed JavaScript and TypeScript sources
--outDir
Since all files will be transpiled, it is necessary to output the resulting JavaScript into a different directory otherwise the input
.js files would be overwritten1.
--checkJs
This is completely optional. If specified, the compiler will typecheck JavaScript files, reporting errors just as in TypeScript files, where as it would otherwise tolerate inconsistencies.
As to using types declared in a TypeScript file in a JavaScript file, this can indeed be done.
TypeScript actually powers all of the JavaScript intellisense in tools like Visual Studio Code.
Types may be placed in JSDoc2 comments. These comments can reference types imported from TypeScript (.ts/.tsx/.d.ts) files. IDEs like Visual Studio Code will provide syntax-highlighting and auto completion within these comments.
There's a caveat however. Because there's no manifest syntax for types in JavaScript, they cannot be imported individually but must be attached to a value which is imported. This is most conveniently achieved via TypeScript's declaration merging as shown below.
Example:
a.ts
export default createThing;
function createThing(...args): createThing.Thing {...}
namespace createThing {
export interface Thing {...}
}
b.js
import createThing from './a';
/**
* #param {createThing.Thing} thing
*/
export function takesThing(thing) {}
Notes:
1: --outDir is not necessary if you additionally specify the --noEmit flag. You would do this when using a tool such as SystemJS (with plugin-typescript) or Webpack (with ts-loader) to host the TypeScript transpiler. The same applies if you are using TS Node.
2: Although called JSDoc comments, they are interpreted in the context of the TypeScript type system, not the JSDoc system. Languages and tools like TypeScript, and Google's Closure Compiler, effectively hijack the JSDoc syntax for their own purposes and thereby confer potentially conflicting meanings to its constructs. This isn't usually a problem but it's worth knowing because it can be difficult to determine the applicability and correctness of these comments and the compatibility of the types that they reference or declare.
Remarks:
Although this question and answer is all about importing types for use in JavaScript files, it's very often unnecessary as the compiler will infer the types from the values of your expressions.
It's also worth mentioning that if you find yourself needing to write a lot of JSDoc style type annotations you are almost certainly better off converting the file to TypeScript as the syntax for expressing types in JSDoc is clumsy. Thanks to the --allowJs option, you can do this on a file by file basis, as described above.

How to use js modules from non-module files

I'm a beginner at using js modules.
I'm working on a fairly simple web application. It uses typescript and angular 2, which heavily relies on modules.
Most of my app ts files 'import' one or many js modules (usually mostly angular 2 modules).
As I understand, because my app ts files have a top level 'import', they are automatically considered a js module by typescript.
However, I want any of my app ts files to be accessible by any other of my app ts files, without having to 'import' each other. But because they are now modules themselves, ts requires me to do that...
Is it possible?
It seems crazy to me that for each of my app ts file, I should have to declare every other of my app ts files that are used in there (I like to have tiny files with a single class/interface). In addition, this relies on relative paths which breaks as soon as I restructure my folder structure.
Am I thinking about this the wrong way?
You must have a js file which is an entry point to your application right?.. So in that file just import all the modules which you want to access without importing and attach them to the window object. Since the window object is available globally, you can access your module from anywhere without importing the corresponding module. For example,
Consider this scenario:
You have a module in a file called module1.ts
The entry point of your application is a file called index.ts
And you have a module2 where you require something from module1
// module1.ts
function add(first: number, second: number): number {
return first + second
}
export {add}
in your index.ts
// index.ts
import {add} from '<path to module1>/module1';
window.add = add
Now in your module2
// module2.ts
window.add(1, 2)
Since the window object is available globally you can attach as many properties to it as you like.
As far as the type resolution is concerned you can declare a window module with the add function you require in a .d.ts file as follows:
declare module window {
add: (first: number, second: number) => number
}
Declaring dependencies (e.g modules) for each file is a double-edged sword.
The advantage is that there is no 'magic' - you know exactly where each function, variable, class etc. is coming from. This makes it much easier to know what libraries / frameworks are being used and where to look to troubleshoot issues. Compare it to opposite approach that Ruby on Rails uses with Ruby Gems, where nothing is declared and everything is auto-loaded. From personal experience I know it becomes an absolute pain to try to workout where some_random_method is coming from and also what methods / classes I have access to.
You're right that the disadvantage is that it can become quite verbose with multiple imports and moving relative files. Modern editors and IDEs like WebStorm and Visual Studio Code have tools to automatically update the relative paths when you move a file and also automatically add the imports when you reference code in another module.
One practical solution for multiple imports is to make your own 'group' import file. Say you have a whole bunch of utility functions that you use in all your files - you can import them all into a single file and then just reference that file everywhere else:
//File: helpers/string-helpers.ts
import {toUppercase} from "./uppercase-helper";
import {truncate} from "./truncate-helper";
export const toUppercase = toUppercase;
export const truncate = truncate;
Then in any other file:
import * as StringHelpers from "../path-to/helpers/string-helpers";
...
let shoutingMessage = StringHelpers.toUppercase(message);
The disadvantage of this is that it may break tree shaking, where tools such as webpack remove unused code.
Is it possible
Not in any easy way. The ts file is a module and uses e.g. module.exports (if commonjs) that will need to be shimmed out. And that is just the runtime story. The TypeScript story will be harder and one way would be to make a .d.ts file for the module stating the contents as global.
Like I said. Not worth doing. Modules are the way forward instead of making something hacky.
It's not crazy at all. You are definitively thinking in the wrong way.
Actually what you don't like it's a common feature in all modern programming languages and it makes the code and structure of the app a lot clearer and simple to understand.
Without imports and going to old school way looks very crazy to me :)
You can have only chaos with so many global variables.

How to use Typescript definitions to get Intellisense for my own Javascript services in VS Code?

I am developing a backend server using SailsJS. It basically injects all model helper services, as well as my own services into the global namespace. It would benefit me greatly if I was able to get Intellisense for those services.
I first set up typings and installed global type definitions for lodash and node. It works like a charm after creating a jsconfig.json and tsconfig.json files.
Next I wanted to create a basic definitions file for my own services. I created a directory in typings/globals with a index.d.ts file in it:
declare namespace foo {
export function bar();
}
declare var baz: { some: number };
This is just to make sure I don't waste time writing definitions if they won't work.
Next I included that index.d.ts file in typings/index.d.ts by adding a reference tag:
/// <reference path="globals/psiewakacje/index.d.ts" />
To my surprise, it does not work in my project's Javascript files. Upon typing foo. or baz. I do not get any sensible Intellisense.
The only Intellisense support I was able to get was when I imported those services in every file via:
import * as InternalParser from '../services/InternalParser';
or
var InternalParser = require('../services/InternalParser');
but this doesn't use Typescript's definition files and just gives me the exported properties. Overall a not desirable result.
I wonder how to get it to work correctly. I looked at node's and lodash's type definition files and they do the same: declare a variable / namespace with a specific type. However, their definitions work in Javascript and mine don't. How to get it right?
I can reproduce the behavior described if I create a tsconfig.json file without the "allowJs": "true" compiler option. If you want your TypeScript and JavaScript files to be considered as a single project, you should have a tsconfig.json file with "allowJs": "true" and no jsconfig.json file.
You don't need to add a reference to typings/index.d.ts. The easiest would be to just add your declarations to a global declaration file, anywhere in your project.
Further, instead of using var and namespace, just use interface.
Eg. in the root of your directory, you can easily add
// index.d.ts
interface Foo {
bar: () => void
}
interface Baz { some: number }
I had the same issue and discovered that the editor (VSCode) was looking in the wrong directory for the file, problem was solved by relative referencing the correct path, the specific path will vary, but in my example it was:
/// <reference path="../../typings/index.d.ts" />
index.d.ts contains the following:
/// <reference path="globals/core-js/index.d.ts" />
/// <reference path="globals/jasmine/index.d.ts" />
/// <reference path="globals/node/index.d.ts" />
and my directory/file structure appears as follows:

How to introduce typescript on a big nodeJS code basis step by step?

I do own quite a big code basis implemented in JavaScript based on NodeJS. I do want to give typescript a try and want to implement all new modules in typescript. Just to see how it performs and how I like the idea. I do need to be able to revert back at any time so I do not want to put to much effort into the migration.
I started by using WebStorm and Gulp to support typescript. I created a new module and used the import / require combination.
import settings = require("./settings");
import _ = require("lodash-node");
By doing so I receive a couple of errors.
Error:(22, 27) TS2307: Cannot find external module './settings'.
Error:(23, 20) TS2307: Cannot find external module 'lodash-node'.
Those modules have no typescript definition file and are plain javascript modules (lodash is obviously a external library). I do have a couple of dependencies and I do not want to create definitions manually even if they are just empty stubs. As mentioned I do want to keep the integration as simple as possible.
Is there a general flag or something like that I can set?
The easiest way to proceed (if you don't want type information for a module) is to write:
declare module "lodash-node" {
var notTyped: any; // naming is unimportant here
export = notTyped;
}
Put that in a separate .d.ts file and /// <reference> it. Then _ will be of type any in your implementation file and the compiler won't complain about not know what module you're talking about.
You can use webpack's typescript-loader and start from there. Make sure you put target: 'node' on your webpack config so you don't have all the browser junk.

Categories